diff --git a/docs/auth/byok.md b/docs/auth/byok.md index 13ad8b055..fd0a8b93e 100644 --- a/docs/auth/byok.md +++ b/docs/auth/byok.md @@ -23,13 +23,13 @@ Azure AI Foundry (formerly Azure OpenAI) is a common BYOK deployment target for ```python import asyncio import os -from copilot import CopilotClient +import copilot FOUNDRY_MODEL_URL = "https://your-resource.openai.azure.com/openai/v1/" # Set FOUNDRY_API_KEY environment variable async def main(): - client = CopilotClient() + client = copilot.cli_client() await client.start() session = await client.create_session({ diff --git a/docs/auth/index.md b/docs/auth/index.md index 9fc65fe28..4c7a585e7 100644 --- a/docs/auth/index.md +++ b/docs/auth/index.md @@ -38,10 +38,10 @@ const client = new CopilotClient(); Python ```python -from copilot import CopilotClient +import copilot # Default: uses logged-in user credentials -client = CopilotClient() +client = copilot.cli_client() await client.start() ``` @@ -106,12 +106,11 @@ const client = new CopilotClient({ Python ```python -from copilot import CopilotClient +import copilot -client = CopilotClient({ - "github_token": user_access_token, # Token from OAuth flow - "use_logged_in_user": False, # Don't use stored CLI credentials -}) +client = copilot.cli_client( + github_token=user_access_token, # Token from OAuth flow +) await client.start() ``` @@ -194,10 +193,10 @@ const client = new CopilotClient(); Python ```python -from copilot import CopilotClient +import copilot # Token is read from environment variable automatically -client = CopilotClient() +client = copilot.cli_client() await client.start() ``` @@ -256,9 +255,7 @@ const client = new CopilotClient({ ```python -client = CopilotClient({ - "use_logged_in_user": False, # Only use explicit tokens -}) +client = copilot.cli_client(use_logged_in_user=False) # Only use explicit tokens ``` diff --git a/docs/debugging.md b/docs/debugging.md index 6183cccdf..28c666876 100644 --- a/docs/debugging.md +++ b/docs/debugging.md @@ -34,9 +34,9 @@ const client = new CopilotClient({ Python ```python -from copilot import CopilotClient +import copilot -client = CopilotClient({"log_level": "debug"}) +client = copilot.cli_client(log_level="debug") ``` @@ -164,7 +164,7 @@ var client = new CopilotClient(new CopilotClientOptions Python ```python - client = CopilotClient({"cli_path": "/usr/local/bin/copilot"}) + client = copilot.cli_client("/usr/local/bin/copilot") ``` @@ -217,7 +217,7 @@ var client = new CopilotClient(new CopilotClientOptions ```python import os - client = CopilotClient({"github_token": os.environ.get("GITHUB_TOKEN")}) + client = copilot.cli_client(github_token=os.environ.get("GITHUB_TOKEN")) ``` diff --git a/docs/getting-started.md b/docs/getting-started.md index 56c6a9c46..d3955d239 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -129,10 +129,10 @@ Create `main.py`: ```python import asyncio -from copilot import CopilotClient +import copilot async def main(): - client = CopilotClient() + client = copilot.cli_client() await client.start() session = await client.create_session({"model": "gpt-4.1"}) @@ -274,11 +274,11 @@ Update `main.py`: ```python import asyncio import sys -from copilot import CopilotClient +import copilot from copilot.generated.session_events import SessionEventType async def main(): - client = CopilotClient() + client = copilot.cli_client() await client.start() session = await client.create_session({ @@ -565,7 +565,7 @@ Update `main.py`: import asyncio import random import sys -from copilot import CopilotClient +import copilot from copilot.tools import define_tool from copilot.generated.session_events import SessionEventType from pydantic import BaseModel, Field @@ -585,7 +585,7 @@ async def get_weather(params: GetWeatherParams) -> dict: return {"city": city, "temperature": f"{temp}°F", "condition": condition} async def main(): - client = CopilotClient() + client = copilot.cli_client() await client.start() session = await client.create_session({ @@ -837,7 +837,7 @@ Create `weather_assistant.py`: import asyncio import random import sys -from copilot import CopilotClient +import copilot from copilot.tools import define_tool from copilot.generated.session_events import SessionEventType from pydantic import BaseModel, Field @@ -854,7 +854,7 @@ async def get_weather(params: GetWeatherParams) -> dict: return {"city": city, "temperature": f"{temp}°F", "condition": condition} async def main(): - client = CopilotClient() + client = copilot.cli_client() await client.start() session = await client.create_session({ @@ -1210,11 +1210,10 @@ const session = await client.createSession({ onPermissionRequest: approveAll }); Python ```python -from copilot import CopilotClient, PermissionHandler +import copilot +from copilot import PermissionHandler -client = CopilotClient({ - "cli_url": "localhost:4321" -}) +client = copilot.network_client("localhost:4321") await client.start() # Use the client normally diff --git a/docs/guides/session-persistence.md b/docs/guides/session-persistence.md index 527f5ecc7..115ab70e7 100644 --- a/docs/guides/session-persistence.md +++ b/docs/guides/session-persistence.md @@ -46,9 +46,9 @@ await session.sendAndWait({ prompt: "Analyze my codebase" }); ### Python ```python -from copilot import CopilotClient +import copilot -client = CopilotClient() +client = copilot.cli_client() await client.start() # Create a session with a meaningful ID diff --git a/docs/guides/setup/azure-managed-identity.md b/docs/guides/setup/azure-managed-identity.md index bfafc6f91..7cd67c9aa 100644 --- a/docs/guides/setup/azure-managed-identity.md +++ b/docs/guides/setup/azure-managed-identity.md @@ -42,7 +42,8 @@ import asyncio import os from azure.identity import DefaultAzureCredential -from copilot import CopilotClient, ProviderConfig, SessionConfig +import copilot +from copilot import ProviderConfig, SessionConfig COGNITIVE_SERVICES_SCOPE = "https://cognitiveservices.azure.com/.default" @@ -54,7 +55,7 @@ async def main(): foundry_url = os.environ["AZURE_AI_FOUNDRY_RESOURCE_URL"] - client = CopilotClient() + client = copilot.cli_client() await client.start() session = await client.create_session( @@ -84,7 +85,8 @@ Bearer tokens expire (typically after ~1 hour). For servers or long-running agen ```python from azure.identity import DefaultAzureCredential -from copilot import CopilotClient, ProviderConfig, SessionConfig +import copilot +from copilot import ProviderConfig, SessionConfig COGNITIVE_SERVICES_SCOPE = "https://cognitiveservices.azure.com/.default" @@ -96,7 +98,7 @@ class ManagedIdentityCopilotAgent: self.foundry_url = foundry_url.rstrip("/") self.model = model self.credential = DefaultAzureCredential() - self.client = CopilotClient() + self.client = copilot.cli_client() def _get_session_config(self) -> SessionConfig: """Build a SessionConfig with a fresh bearer token.""" diff --git a/docs/guides/setup/backend-services.md b/docs/guides/setup/backend-services.md index c9bc13f8d..ed5d07e76 100644 --- a/docs/guides/setup/backend-services.md +++ b/docs/guides/setup/backend-services.md @@ -111,11 +111,9 @@ res.json({ content: response?.data.content }); Python ```python -from copilot import CopilotClient +import copilot -client = CopilotClient({ - "cli_url": "localhost:4321", -}) +client = copilot.network_client("localhost:4321") await client.start() session = await client.create_session({ diff --git a/docs/guides/setup/bundled-cli.md b/docs/guides/setup/bundled-cli.md index 6daf57b56..74fe507aa 100644 --- a/docs/guides/setup/bundled-cli.md +++ b/docs/guides/setup/bundled-cli.md @@ -85,12 +85,10 @@ await client.stop(); Python ```python -from copilot import CopilotClient +import copilot from pathlib import Path -client = CopilotClient({ - "cli_path": str(Path(__file__).parent / "vendor" / "copilot"), -}) +client = copilot.cli_client(str(Path(__file__).parent / "vendor" / "copilot")) await client.start() session = await client.create_session({"model": "gpt-4.1"}) diff --git a/docs/guides/setup/byok.md b/docs/guides/setup/byok.md index 5b8b8a460..3ae24dfb1 100644 --- a/docs/guides/setup/byok.md +++ b/docs/guides/setup/byok.md @@ -93,9 +93,9 @@ await client.stop(); ```python import os -from copilot import CopilotClient +import copilot -client = CopilotClient() +client = copilot.cli_client() await client.start() session = await client.create_session({ diff --git a/docs/guides/setup/github-oauth.md b/docs/guides/setup/github-oauth.md index 07251c8fb..5482f7f24 100644 --- a/docs/guides/setup/github-oauth.md +++ b/docs/guides/setup/github-oauth.md @@ -145,13 +145,13 @@ const response = await session.sendAndWait({ prompt: "Hello!" }); Python ```python +import copilot from copilot import CopilotClient def create_client_for_user(user_token: str) -> CopilotClient: - return CopilotClient({ - "github_token": user_token, - "use_logged_in_user": False, - }) + return copilot.cli_client( + github_token=user_token, + ) # Usage client = create_client_for_user("gho_user_access_token") diff --git a/docs/guides/setup/local-cli.md b/docs/guides/setup/local-cli.md index a5fa906b8..8becd1fd3 100644 --- a/docs/guides/setup/local-cli.md +++ b/docs/guides/setup/local-cli.md @@ -51,9 +51,9 @@ await client.stop(); Python ```python -from copilot import CopilotClient +import copilot -client = CopilotClient() +client = copilot.cli_client() await client.start() session = await client.create_session({"model": "gpt-4.1"}) diff --git a/docs/guides/skills.md b/docs/guides/skills.md index b2ea3ae7a..79305bddb 100644 --- a/docs/guides/skills.md +++ b/docs/guides/skills.md @@ -42,10 +42,10 @@ await session.sendAndWait({ prompt: "Review this code for security issues" }); Python ```python -from copilot import CopilotClient +import copilot async def main(): - client = CopilotClient() + client = copilot.cli_client() await client.start() session = await client.create_session({ diff --git a/docs/hooks/overview.md b/docs/hooks/overview.md index a51ef0464..b57fd9e96 100644 --- a/docs/hooks/overview.md +++ b/docs/hooks/overview.md @@ -53,10 +53,10 @@ const session = await client.createSession({ Python ```python -from copilot import CopilotClient +import copilot async def main(): - client = CopilotClient() + client = copilot.cli_client() await client.start() async def on_pre_tool_use(input_data, invocation): diff --git a/docs/mcp/overview.md b/docs/mcp/overview.md index aa2fba668..3f43482cc 100644 --- a/docs/mcp/overview.md +++ b/docs/mcp/overview.md @@ -59,10 +59,10 @@ const session = await client.createSession({ ```python import asyncio -from copilot import CopilotClient +import copilot async def main(): - client = CopilotClient() + client = copilot.cli_client() await client.start() session = await client.create_session({ diff --git a/python/README.md b/python/README.md index aa82e0c34..b8ee61215 100644 --- a/python/README.md +++ b/python/README.md @@ -25,11 +25,11 @@ python chat.py ```python import asyncio -from copilot import CopilotClient +import copilot async def main(): # Create and start client - client = CopilotClient() + client = copilot.cli_client() await client.start() # Create a session @@ -68,16 +68,54 @@ asyncio.run(main()) ## API Reference -### CopilotClient +### Client Creation + +The SDK provides two factory functions to create a client: + +**`copilot.cli_client()`** — Spawns and manages a local CLI process: + +```python +import copilot + +# Default: uses bundled CLI binary with stdio transport +client = copilot.cli_client() + +# Custom CLI path with extra arguments +client = copilot.cli_client("/usr/local/bin/copilot", "--flag") + +# With authentication token +client = copilot.cli_client(github_token="gho_xxx") +``` + +Parameters: +- `path` (str, optional): Path to CLI executable (default: bundled binary). +- `*args` (str): Extra arguments passed to the CLI executable. +- `cwd` (str): Working directory for the CLI process (default: current directory). +- `port` (int): Server port for TCP mode (default: `0` for random). +- `use_stdio` (bool): Use stdio transport instead of TCP (default: `True`). +- `log_level` (str): Log level for the CLI server (default: `"info"`). +- `auto_start` (bool): Auto-connect on first use (default: `True`). +- `auto_restart` (bool): Auto-restart on crash (default: `True`). +- `env` (dict[str, str]): Environment variables for the CLI process. +- `github_token` (str): GitHub token for authentication. +- `use_logged_in_user` (bool): Use stored OAuth / gh CLI auth (default: `True`, `False` when `github_token` is set). + +**`copilot.network_client()`** — Connects to an existing CLI server: + +```python +import copilot + +client = copilot.network_client("localhost:8080") +client = copilot.network_client("http://127.0.0.1:9000") +client = copilot.network_client("8080") # defaults to localhost +``` + +Parameters: +- `cli_url` (str, required): URL of the CLI server. +- `log_level` (str): Log level (default: `"info"`). +- `auto_start` (bool): Auto-connect on first use (default: `True`). ```python -client = CopilotClient({ - "cli_path": "copilot", # Optional: path to CLI executable - "cli_url": None, # Optional: URL of existing server (e.g., "localhost:8080") - "log_level": "info", # Optional: log level (default: "info") - "auto_start": True, # Optional: auto-start server (default: True) - "auto_restart": True, # Optional: auto-restart on crash (default: True) -}) await client.start() session = await client.create_session({"model": "gpt-5"}) @@ -94,19 +132,6 @@ await session.destroy() await client.stop() ``` -**CopilotClient Options:** - -- `cli_path` (str): Path to CLI executable (default: "copilot" or `COPILOT_CLI_PATH` env var) -- `cli_url` (str): URL of existing CLI server (e.g., `"localhost:8080"`, `"http://127.0.0.1:9000"`, or just `"8080"`). When provided, the client will not spawn a CLI process. -- `cwd` (str): Working directory for CLI process -- `port` (int): Server port for TCP mode (default: 0 for random) -- `use_stdio` (bool): Use stdio transport instead of TCP (default: True) -- `log_level` (str): Log level (default: "info") -- `auto_start` (bool): Auto-start server on first use (default: True) -- `auto_restart` (bool): Auto-restart on crash (default: True) -- `github_token` (str): GitHub token for authentication. When provided, takes priority over other auth methods. -- `use_logged_in_user` (bool): Whether to use logged-in user for authentication (default: True, but False when `github_token` is provided). Cannot be used with `cli_url`. - **SessionConfig Options (for `create_session`):** - `model` (str): Model to use ("gpt-5", "claude-sonnet-4.5", etc.). **Required when using custom provider.** @@ -155,7 +180,7 @@ Define tools with automatic JSON schema generation using the `@define_tool` deco ```python from pydantic import BaseModel, Field -from copilot import CopilotClient, define_tool +from copilot import define_tool class LookupIssueParams(BaseModel): id: str = Field(description="Issue identifier") @@ -178,7 +203,7 @@ session = await client.create_session({ For users who prefer manual schema definition: ```python -from copilot import CopilotClient, Tool +from copilot import Tool async def lookup_issue(invocation): issue_id = invocation["arguments"]["id"] @@ -238,10 +263,10 @@ Enable streaming to receive assistant response chunks as they're generated: ```python import asyncio -from copilot import CopilotClient +import copilot async def main(): - client = CopilotClient() + client = copilot.cli_client() await client.start() session = await client.create_session({ diff --git a/python/copilot/__init__.py b/python/copilot/__init__.py index f5f7ed0b1..4d914a812 100644 --- a/python/copilot/__init__.py +++ b/python/copilot/__init__.py @@ -4,7 +4,9 @@ JSON-RPC based SDK for programmatic control of GitHub Copilot CLI """ -from .client import CopilotClient +from __future__ import annotations + +from .client import CopilotClient, cli_client, network_client from .session import CopilotSession from .tools import define_tool from .types import ( @@ -13,6 +15,7 @@ CustomAgentConfig, GetAuthStatusResponse, GetStatusResponse, + LogLevel, MCPLocalServerConfig, MCPRemoteServerConfig, MCPServerConfig, @@ -41,6 +44,7 @@ __version__ = "0.1.0" + __all__ = [ "AzureProviderOptions", "CopilotClient", @@ -49,6 +53,7 @@ "CustomAgentConfig", "GetAuthStatusResponse", "GetStatusResponse", + "LogLevel", "MCPLocalServerConfig", "MCPRemoteServerConfig", "MCPServerConfig", @@ -73,5 +78,7 @@ "ToolHandler", "ToolInvocation", "ToolResult", + "cli_client", "define_tool", + "network_client", ] diff --git a/python/copilot/client.py b/python/copilot/client.py index 774569afb..88ac25a21 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -5,13 +5,18 @@ to the Copilot CLI server and provides session management capabilities. Example: - >>> from copilot import CopilotClient + >>> import copilot >>> - >>> async with CopilotClient() as client: - ... session = await client.create_session() - ... await session.send({"prompt": "Hello!"}) + >>> client = copilot.cli_client() + >>> await client.start() + >>> session = await client.create_session() + >>> await session.send({"prompt": "Hello!"}) + >>> await session.destroy() + >>> await client.stop() """ +from __future__ import annotations + import asyncio import inspect import os @@ -21,7 +26,7 @@ import threading from dataclasses import asdict, is_dataclass from pathlib import Path -from typing import Any, Callable, Optional, cast +from typing import Any, Callable, Optional, Union, cast from .generated.rpc import ServerRpc from .generated.session_events import session_event_from_dict @@ -30,10 +35,10 @@ from .session import CopilotSession from .types import ( ConnectionState, - CopilotClientOptions, CustomAgentConfig, GetAuthStatusResponse, GetStatusResponse, + LogLevel, ModelInfo, PingResponse, ProviderConfig, @@ -48,6 +53,8 @@ ToolHandler, ToolInvocation, ToolResult, + _CLIClientConfig, + _NetworkClientConfig, ) @@ -79,123 +86,81 @@ class CopilotClient: methods to create and manage conversation sessions. It can either spawn a CLI server process or connect to an existing server. - The client supports both stdio (default) and TCP transport modes for - communication with the CLI server. - - Attributes: - options: The configuration options for the client. + Use the factory functions :func:`copilot.cli_client` and + :func:`copilot.network_client` to create instances. Example: - >>> # Create a client with default options (spawns CLI server) - >>> client = CopilotClient() - >>> await client.start() + >>> import copilot >>> - >>> # Create a session and send a message - >>> session = await client.create_session({ - ... "on_permission_request": PermissionHandler.approve_all, - ... "model": "gpt-4", - ... }) - >>> session.on(lambda event: print(event.type)) - >>> await session.send({"prompt": "Hello!"}) + >>> # Create a client that spawns a CLI server + >>> client = copilot.cli_client() + >>> await client.start() >>> - >>> # Clean up - >>> await session.destroy() - >>> await client.stop() - >>> # Or connect to an existing server - >>> client = CopilotClient({"cli_url": "localhost:3000"}) + >>> client = copilot.network_client("localhost:3000") """ - def __init__(self, options: Optional[CopilotClientOptions] = None): + def __init__(self, config: Union[_CLIClientConfig, _NetworkClientConfig]): """ Initialize a new CopilotClient. Args: - options: Optional configuration options for the client. If not provided, - default options are used (spawns CLI server using stdio). - - Raises: - ValueError: If mutually exclusive options are provided (e.g., cli_url - with use_stdio or cli_path). - - Example: - >>> # Default options - spawns CLI server using stdio - >>> client = CopilotClient() - >>> - >>> # Connect to an existing server - >>> client = CopilotClient({"cli_url": "localhost:3000"}) - >>> - >>> # Custom CLI path with specific log level - >>> client = CopilotClient({ - ... "cli_path": "/usr/local/bin/copilot", - ... "log_level": "debug" - ... }) + config: A ``_CLIClientConfig`` or ``_NetworkClientConfig`` instance. + Use :func:`copilot.cli_client` or :func:`copilot.network_client` + instead of constructing directly. """ - opts = options or {} - - # Validate mutually exclusive options - if opts.get("cli_url") and (opts.get("use_stdio") or opts.get("cli_path")): - raise ValueError("cli_url is mutually exclusive with use_stdio and cli_path") + self._config = config - # Validate auth options with external server - if opts.get("cli_url") and ( - opts.get("github_token") or opts.get("use_logged_in_user") is not None - ): - raise ValueError( - "github_token and use_logged_in_user cannot be used with cli_url " - "(external server manages its own auth)" - ) - - # Parse cli_url if provided self._actual_host: str = "localhost" self._is_external_server: bool = False - if opts.get("cli_url"): - self._actual_host, actual_port = self._parse_cli_url(opts["cli_url"]) + + if isinstance(config, _NetworkClientConfig): + self._actual_host, actual_port = self._parse_cli_url(config.cli_url) self._actual_port: Optional[int] = actual_port self._is_external_server = True + + self._cli_path = "" + self._cli_args: list[str] = [] + self._cwd = os.getcwd() + self._port = 0 + self._use_stdio = False + self._log_level = config.log_level + self._auto_start = config.auto_start + self._auto_restart = False + self._env: dict[str, str] | None = None + self._github_token: str | None = None + self._use_logged_in_user = True else: self._actual_port = None - # Determine CLI path: explicit option > bundled binary - # Not needed when connecting to external server via cli_url - if opts.get("cli_url"): - default_cli_path = "" # Not used for external server - elif opts.get("cli_path"): - default_cli_path = opts["cli_path"] - else: - bundled_path = _get_bundled_cli_path() - if bundled_path: - default_cli_path = bundled_path + # Determine CLI path: explicit option > bundled binary + if config.cli_path: + self._cli_path = config.cli_path else: - raise RuntimeError( - "Copilot CLI not found. The bundled CLI binary is not available. " - "Ensure you installed a platform-specific wheel, or provide cli_path." - ) - - # Default use_logged_in_user to False when github_token is provided - github_token = opts.get("github_token") - use_logged_in_user = opts.get("use_logged_in_user") - if use_logged_in_user is None: - use_logged_in_user = False if github_token else True - - self.options: CopilotClientOptions = { - "cli_path": default_cli_path, - "cwd": opts.get("cwd", os.getcwd()), - "port": opts.get("port", 0), - "use_stdio": False if opts.get("cli_url") else opts.get("use_stdio", True), - "log_level": opts.get("log_level", "info"), - "auto_start": opts.get("auto_start", True), - "auto_restart": opts.get("auto_restart", True), - "use_logged_in_user": use_logged_in_user, - } - if opts.get("cli_args"): - self.options["cli_args"] = opts["cli_args"] - if opts.get("cli_url"): - self.options["cli_url"] = opts["cli_url"] - if opts.get("env"): - self.options["env"] = opts["env"] - if github_token: - self.options["github_token"] = github_token + bundled_path = _get_bundled_cli_path() + if bundled_path: + self._cli_path = bundled_path + else: + raise RuntimeError( + "Copilot CLI not found. The bundled CLI binary is not available. " + "Ensure you installed a platform-specific wheel, or provide a path." + ) + + self._cli_args = list(config.cli_args) if config.cli_args else [] + self._cwd = config.cwd or os.getcwd() + self._port = config.port + self._use_stdio = config.use_stdio + self._log_level = config.log_level + self._auto_start = config.auto_start + self._auto_restart = config.auto_restart + self._env = config.env + self._github_token = config.github_token + + # Default use_logged_in_user to False when github_token is provided + if config.use_logged_in_user is None: + self._use_logged_in_user = False if config.github_token else True + else: + self._use_logged_in_user = config.use_logged_in_user self._process: Optional[subprocess.Popen] = None self._client: Optional[JsonRpcClient] = None @@ -448,7 +413,7 @@ async def create_session(self, config: SessionConfig) -> CopilotSession: ... }) """ if not self._client: - if self.options["auto_start"]: + if self._auto_start: await self.start() else: raise RuntimeError("Client not connected. Call start() first.") @@ -620,7 +585,7 @@ async def resume_session(self, session_id: str, config: ResumeSessionConfig) -> ... }) """ if not self._client: - if self.options["auto_start"]: + if self._auto_start: await self.start() else: raise RuntimeError("Client not connected. Call start() first.") @@ -1174,25 +1139,24 @@ async def _start_cli_server(self) -> None: Raises: RuntimeError: If the server fails to start or times out. """ - cli_path = self.options["cli_path"] + cli_path = self._cli_path # Verify CLI exists if not os.path.exists(cli_path): raise RuntimeError(f"Copilot CLI not found at {cli_path}") # Start with user-provided cli_args, then add SDK-managed args - cli_args = self.options.get("cli_args") or [] - args = list(cli_args) + [ + args = list(self._cli_args) + [ "--headless", "--no-auto-update", "--log-level", - self.options["log_level"], + self._log_level, ] # Add auth-related flags - if self.options.get("github_token"): + if self._github_token: args.extend(["--auth-token-env", "COPILOT_SDK_AUTH_TOKEN"]) - if not self.options.get("use_logged_in_user", True): + if not self._use_logged_in_user: args.append("--no-auto-login") # If cli_path is a .js file, run it with node @@ -1203,21 +1167,21 @@ async def _start_cli_server(self) -> None: args = [cli_path] + args # Get environment variables - env = self.options.get("env") + env = self._env if env is None: env = dict(os.environ) else: env = dict(env) # Set auth token in environment if provided - if self.options.get("github_token"): - env["COPILOT_SDK_AUTH_TOKEN"] = self.options["github_token"] + if self._github_token: + env["COPILOT_SDK_AUTH_TOKEN"] = self._github_token # On Windows, hide the console window to avoid distracting users in GUI apps creationflags = subprocess.CREATE_NO_WINDOW if sys.platform == "win32" else 0 # Choose transport mode - if self.options["use_stdio"]: + if self._use_stdio: args.append("--stdio") # Use regular Popen with pipes (buffering=0 for unbuffered) self._process = subprocess.Popen( @@ -1226,25 +1190,25 @@ async def _start_cli_server(self) -> None: stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0, - cwd=self.options["cwd"], + cwd=self._cwd, env=env, creationflags=creationflags, ) else: - if self.options["port"] > 0: - args.extend(["--port", str(self.options["port"])]) + if self._port > 0: + args.extend(["--port", str(self._port)]) self._process = subprocess.Popen( args, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - cwd=self.options["cwd"], + cwd=self._cwd, env=env, creationflags=creationflags, ) # For stdio mode, we're ready immediately - if self.options["use_stdio"]: + if self._use_stdio: return # For TCP mode, wait for port announcement @@ -1279,7 +1243,7 @@ async def _connect_to_server(self) -> None: Raises: RuntimeError: If the connection fails. """ - if self.options["use_stdio"]: + if self._use_stdio: await self._connect_via_stdio() else: await self._connect_via_tcp() @@ -1623,3 +1587,88 @@ def _build_unsupported_tool_result(self, tool_name: str) -> ToolResult: error=f"tool '{tool_name}' not supported", toolTelemetry={}, ) + + +def cli_client( + path: str | None = None, + *args: str, + cwd: str | None = None, + port: int = 0, + use_stdio: bool = True, + log_level: LogLevel = "info", + auto_start: bool = True, + auto_restart: bool = True, + env: dict[str, str] | None = None, + github_token: str | None = None, + use_logged_in_user: bool | None = None, +) -> CopilotClient: + """Create a :class:`CopilotClient` that spawns and manages a CLI process. + + Args: + path: Path to the Copilot CLI executable. If not provided, the bundled + CLI binary is used. + *args: Extra arguments passed to the CLI executable (inserted before + SDK-managed args). + cwd: Working directory for the CLI process (default: current directory). + port: Port for the CLI server in TCP mode (default: 0 = random). + use_stdio: Use stdio transport instead of TCP (default: True). + log_level: Log level for the CLI server. + auto_start: Auto-start the CLI server on first use (default: True). + auto_restart: Auto-restart the CLI server if it crashes (default: True). + env: Environment variables for the CLI process. + github_token: GitHub token for authentication. Takes priority over + other authentication methods. + use_logged_in_user: Whether to use stored OAuth / gh CLI auth. + Defaults to True, but False when *github_token* is provided. + + Example:: + + import copilot + + client = copilot.cli_client() + client = copilot.cli_client("/usr/local/bin/copilot", "--flag") + client = copilot.cli_client(github_token="gho_xxx") + """ + config = _CLIClientConfig( + cli_path=path, + cli_args=list(args) or None, + cwd=cwd, + port=port, + use_stdio=use_stdio, + log_level=log_level, + auto_start=auto_start, + auto_restart=auto_restart, + env=env, + github_token=github_token, + use_logged_in_user=use_logged_in_user, + ) + return CopilotClient(config) + + +def network_client( + cli_url: str, + *, + log_level: LogLevel = "info", + auto_start: bool = True, +) -> CopilotClient: + """Create a :class:`CopilotClient` that connects to an existing CLI server. + + Args: + cli_url: URL of the Copilot CLI server. + Formats: ``"host:port"``, ``"http://host:port"``, or ``"port"``. + log_level: Log level for the client. + auto_start: Auto-connect on first use (default: True). + + Example:: + + import copilot + + client = copilot.network_client("localhost:3000") + client = copilot.network_client("8080") + """ + config = _NetworkClientConfig( + cli_url=cli_url, + log_level=log_level, + auto_start=auto_start, + ) + return CopilotClient(config) diff --git a/python/copilot/types.py b/python/copilot/types.py index 142aee474..4d588c41e 100644 --- a/python/copilot/types.py +++ b/python/copilot/types.py @@ -68,35 +68,30 @@ class SelectionAttachment(TypedDict): Attachment = Union[FileAttachment, DirectoryAttachment, SelectionAttachment] -# Options for creating a CopilotClient -class CopilotClientOptions(TypedDict, total=False): - """Options for creating a CopilotClient""" - - cli_path: str # Path to the Copilot CLI executable (default: "copilot") - # Extra arguments to pass to the CLI executable (inserted before SDK-managed args) - cli_args: list[str] - # Working directory for the CLI process (default: current process's cwd) - cwd: str - port: int # Port for the CLI server (TCP mode only, default: 0) - use_stdio: bool # Use stdio transport instead of TCP (default: True) - cli_url: str # URL of an existing Copilot CLI server to connect to over TCP - # Format: "host:port" or "http://host:port" or just "port" (defaults to localhost) - # Examples: "localhost:8080", "http://127.0.0.1:9000", "8080" - # Mutually exclusive with cli_path, use_stdio - log_level: LogLevel # Log level - auto_start: bool # Auto-start the CLI server on first use (default: True) - # Auto-restart the CLI server if it crashes (default: True) - auto_restart: bool - env: dict[str, str] # Environment variables for the CLI process - # GitHub token to use for authentication. - # When provided, the token is passed to the CLI server via environment variable. - # This takes priority over other authentication methods. - github_token: str - # Whether to use the logged-in user for authentication. - # When True, the CLI server will attempt to use stored OAuth tokens or gh CLI auth. - # When False, only explicit tokens (github_token or environment variables) are used. - # Default: True (but defaults to False when github_token is provided) - use_logged_in_user: bool +@dataclass +class _CLIClientConfig: + """Configuration for a CopilotClient that spawns and manages a CLI process.""" + + cli_path: str | None = None + cli_args: list[str] | None = None + cwd: str | None = None + port: int = 0 + use_stdio: bool = True + log_level: LogLevel = "info" + auto_start: bool = True + auto_restart: bool = True + env: dict[str, str] | None = None + github_token: str | None = None + use_logged_in_user: bool | None = None + + +@dataclass +class _NetworkClientConfig: + """Configuration for a CopilotClient that connects to an existing CLI server.""" + + cli_url: str + log_level: LogLevel = "info" + auto_start: bool = True ToolResultType = Literal["success", "failure", "rejected", "denied"] diff --git a/python/e2e/test_agent_and_compact_rpc.py b/python/e2e/test_agent_and_compact_rpc.py index a960c8426..88ce6a48b 100644 --- a/python/e2e/test_agent_and_compact_rpc.py +++ b/python/e2e/test_agent_and_compact_rpc.py @@ -2,7 +2,8 @@ import pytest -from copilot import CopilotClient, PermissionHandler +import copilot +from copilot import PermissionHandler from copilot.generated.rpc import SessionAgentSelectParams from .testharness import CLI_PATH, E2ETestContext @@ -14,7 +15,7 @@ class TestAgentSelectionRpc: @pytest.mark.asyncio async def test_should_list_available_custom_agents(self): """Test listing available custom agents via RPC.""" - client = CopilotClient({"cli_path": CLI_PATH, "use_stdio": True}) + client = copilot.cli_client(CLI_PATH) try: await client.start() @@ -54,7 +55,7 @@ async def test_should_list_available_custom_agents(self): @pytest.mark.asyncio async def test_should_return_null_when_no_agent_is_selected(self): """Test getCurrent returns null when no agent is selected.""" - client = CopilotClient({"cli_path": CLI_PATH, "use_stdio": True}) + client = copilot.cli_client(CLI_PATH) try: await client.start() @@ -83,7 +84,7 @@ async def test_should_return_null_when_no_agent_is_selected(self): @pytest.mark.asyncio async def test_should_select_and_get_current_agent(self): """Test selecting an agent and verifying getCurrent returns it.""" - client = CopilotClient({"cli_path": CLI_PATH, "use_stdio": True}) + client = copilot.cli_client(CLI_PATH) try: await client.start() @@ -122,7 +123,7 @@ async def test_should_select_and_get_current_agent(self): @pytest.mark.asyncio async def test_should_deselect_current_agent(self): """Test deselecting the current agent.""" - client = CopilotClient({"cli_path": CLI_PATH, "use_stdio": True}) + client = copilot.cli_client(CLI_PATH) try: await client.start() @@ -156,7 +157,7 @@ async def test_should_deselect_current_agent(self): @pytest.mark.asyncio async def test_should_return_empty_list_when_no_custom_agents_configured(self): """Test listing agents returns empty when none configured.""" - client = CopilotClient({"cli_path": CLI_PATH, "use_stdio": True}) + client = copilot.cli_client(CLI_PATH) try: await client.start() diff --git a/python/e2e/test_client.py b/python/e2e/test_client.py index cc5d31ac6..f2ea60f91 100644 --- a/python/e2e/test_client.py +++ b/python/e2e/test_client.py @@ -2,7 +2,8 @@ import pytest -from copilot import CopilotClient, PermissionHandler +import copilot +from copilot import PermissionHandler from .testharness import CLI_PATH @@ -10,7 +11,7 @@ class TestClient: @pytest.mark.asyncio async def test_should_start_and_connect_to_server_using_stdio(self): - client = CopilotClient({"cli_path": CLI_PATH, "use_stdio": True}) + client = copilot.cli_client(CLI_PATH) try: await client.start() @@ -28,7 +29,7 @@ async def test_should_start_and_connect_to_server_using_stdio(self): @pytest.mark.asyncio async def test_should_start_and_connect_to_server_using_tcp(self): - client = CopilotClient({"cli_path": CLI_PATH, "use_stdio": False}) + client = copilot.cli_client(CLI_PATH, use_stdio=False) try: await client.start() @@ -48,7 +49,7 @@ async def test_should_start_and_connect_to_server_using_tcp(self): async def test_should_return_errors_on_failed_cleanup(self): import asyncio - client = CopilotClient({"cli_path": CLI_PATH}) + client = copilot.cli_client(CLI_PATH) try: await client.create_session({"on_permission_request": PermissionHandler.approve_all}) @@ -67,7 +68,7 @@ async def test_should_return_errors_on_failed_cleanup(self): @pytest.mark.asyncio async def test_should_force_stop_without_cleanup(self): - client = CopilotClient({"cli_path": CLI_PATH}) + client = copilot.cli_client(CLI_PATH) await client.create_session({"on_permission_request": PermissionHandler.approve_all}) await client.force_stop() @@ -75,7 +76,7 @@ async def test_should_force_stop_without_cleanup(self): @pytest.mark.asyncio async def test_should_get_status_with_version_and_protocol_info(self): - client = CopilotClient({"cli_path": CLI_PATH, "use_stdio": True}) + client = copilot.cli_client(CLI_PATH) try: await client.start() @@ -93,7 +94,7 @@ async def test_should_get_status_with_version_and_protocol_info(self): @pytest.mark.asyncio async def test_should_get_auth_status(self): - client = CopilotClient({"cli_path": CLI_PATH, "use_stdio": True}) + client = copilot.cli_client(CLI_PATH) try: await client.start() @@ -111,7 +112,7 @@ async def test_should_get_auth_status(self): @pytest.mark.asyncio async def test_should_list_models_when_authenticated(self): - client = CopilotClient({"cli_path": CLI_PATH, "use_stdio": True}) + client = copilot.cli_client(CLI_PATH) try: await client.start() @@ -139,7 +140,7 @@ async def test_should_list_models_when_authenticated(self): @pytest.mark.asyncio async def test_should_cache_models_list(self): """Test that list_models caches results to avoid rate limiting""" - client = CopilotClient({"cli_path": CLI_PATH, "use_stdio": True}) + client = copilot.cli_client(CLI_PATH) try: await client.start() @@ -183,13 +184,7 @@ async def test_should_cache_models_list(self): @pytest.mark.asyncio async def test_should_report_error_with_stderr_when_cli_fails_to_start(self): """Test that CLI startup errors include stderr output in the error message.""" - client = CopilotClient( - { - "cli_path": CLI_PATH, - "cli_args": ["--nonexistent-flag-for-testing"], - "use_stdio": True, - } - ) + client = copilot.cli_client(CLI_PATH, "--nonexistent-flag-for-testing") try: with pytest.raises(RuntimeError) as exc_info: diff --git a/python/e2e/test_rpc.py b/python/e2e/test_rpc.py index 240cd3730..d81bc4862 100644 --- a/python/e2e/test_rpc.py +++ b/python/e2e/test_rpc.py @@ -2,7 +2,8 @@ import pytest -from copilot import CopilotClient, PermissionHandler +import copilot +from copilot import PermissionHandler from copilot.generated.rpc import PingParams from .testharness import CLI_PATH, E2ETestContext @@ -14,7 +15,7 @@ class TestRpc: @pytest.mark.asyncio async def test_should_call_rpc_ping_with_typed_params(self): """Test calling rpc.ping with typed params and result""" - client = CopilotClient({"cli_path": CLI_PATH, "use_stdio": True}) + client = copilot.cli_client(CLI_PATH) try: await client.start() @@ -30,7 +31,7 @@ async def test_should_call_rpc_ping_with_typed_params(self): @pytest.mark.asyncio async def test_should_call_rpc_models_list(self): """Test calling rpc.models.list with typed result""" - client = CopilotClient({"cli_path": CLI_PATH, "use_stdio": True}) + client = copilot.cli_client(CLI_PATH) try: await client.start() @@ -53,7 +54,7 @@ async def test_should_call_rpc_models_list(self): @pytest.mark.asyncio async def test_should_call_rpc_account_get_quota(self): """Test calling rpc.account.getQuota when authenticated""" - client = CopilotClient({"cli_path": CLI_PATH, "use_stdio": True}) + client = copilot.cli_client(CLI_PATH) try: await client.start() @@ -112,7 +113,7 @@ async def test_get_and_set_session_mode(self): """Test getting and setting session mode""" from copilot.generated.rpc import Mode, SessionModeSetParams - client = CopilotClient({"cli_path": CLI_PATH, "use_stdio": True}) + client = copilot.cli_client(CLI_PATH) try: await client.start() @@ -148,7 +149,7 @@ async def test_read_update_and_delete_plan(self): """Test reading, updating, and deleting plan""" from copilot.generated.rpc import SessionPlanUpdateParams - client = CopilotClient({"cli_path": CLI_PATH, "use_stdio": True}) + client = copilot.cli_client(CLI_PATH) try: await client.start() @@ -191,7 +192,7 @@ async def test_create_list_and_read_workspace_files(self): SessionWorkspaceReadFileParams, ) - client = CopilotClient({"cli_path": CLI_PATH, "use_stdio": True}) + client = copilot.cli_client(CLI_PATH) try: await client.start() diff --git a/python/e2e/test_session.py b/python/e2e/test_session.py index 4842d7829..8e4e61963 100644 --- a/python/e2e/test_session.py +++ b/python/e2e/test_session.py @@ -4,7 +4,8 @@ import pytest -from copilot import CopilotClient, PermissionHandler +import copilot +from copilot import PermissionHandler from copilot.types import Tool from .testharness import E2ETestContext, get_final_assistant_message, get_next_event_of_type @@ -184,13 +185,11 @@ async def test_should_resume_a_session_using_a_new_client(self, ctx: E2ETestCont # Resume using a new client github_token = "fake-token-for-e2e-tests" if os.environ.get("CI") == "true" else None - new_client = CopilotClient( - { - "cli_path": ctx.cli_path, - "cwd": ctx.work_dir, - "env": ctx.get_env(), - "github_token": github_token, - } + new_client = copilot.cli_client( + ctx.cli_path, + cwd=ctx.work_dir, + env=ctx.get_env(), + github_token=github_token, ) try: diff --git a/python/e2e/testharness/context.py b/python/e2e/testharness/context.py index 4417f567d..7b9255a12 100644 --- a/python/e2e/testharness/context.py +++ b/python/e2e/testharness/context.py @@ -11,6 +11,7 @@ from pathlib import Path from typing import Optional +import copilot from copilot import CopilotClient from .proxy import CapiProxy @@ -62,13 +63,11 @@ async def setup(self): # Create the shared client (like Node.js/Go do) # Use fake token in CI to allow cached responses without real auth github_token = "fake-token-for-e2e-tests" if os.environ.get("CI") == "true" else None - self._client = CopilotClient( - { - "cli_path": self.cli_path, - "cwd": self.work_dir, - "env": self.get_env(), - "github_token": github_token, - } + self._client = copilot.cli_client( + self.cli_path, + cwd=self.work_dir, + env=self.get_env(), + github_token=github_token, ) async def teardown(self, test_failed: bool = False): diff --git a/python/samples/chat.py b/python/samples/chat.py index eb781e4e2..1deb78751 100644 --- a/python/samples/chat.py +++ b/python/samples/chat.py @@ -1,13 +1,14 @@ import asyncio -from copilot import CopilotClient, PermissionHandler +import copilot +from copilot import PermissionHandler BLUE = "\033[34m" RESET = "\033[0m" async def main(): - client = CopilotClient() + client = copilot.cli_client() await client.start() session = await client.create_session( { diff --git a/python/test_client.py b/python/test_client.py index c6ad027f5..6aac63004 100644 --- a/python/test_client.py +++ b/python/test_client.py @@ -6,14 +6,15 @@ import pytest -from copilot import CopilotClient, PermissionHandler +import copilot +from copilot import PermissionHandler from e2e.testharness import CLI_PATH class TestPermissionHandlerRequired: @pytest.mark.asyncio async def test_create_session_raises_without_permission_handler(self): - client = CopilotClient({"cli_path": CLI_PATH}) + client = copilot.cli_client(CLI_PATH) await client.start() try: with pytest.raises(ValueError, match="on_permission_request.*is required"): @@ -23,7 +24,7 @@ async def test_create_session_raises_without_permission_handler(self): @pytest.mark.asyncio async def test_resume_session_raises_without_permission_handler(self): - client = CopilotClient({"cli_path": CLI_PATH}) + client = copilot.cli_client(CLI_PATH) await client.start() try: session = await client.create_session( @@ -38,7 +39,7 @@ async def test_resume_session_raises_without_permission_handler(self): class TestHandleToolCallRequest: @pytest.mark.asyncio async def test_returns_failure_when_tool_not_registered(self): - client = CopilotClient({"cli_path": CLI_PATH}) + client = copilot.cli_client(CLI_PATH) await client.start() try: @@ -63,123 +64,85 @@ async def test_returns_failure_when_tool_not_registered(self): class TestURLParsing: def test_parse_port_only_url(self): - client = CopilotClient({"cli_url": "8080", "log_level": "error"}) + client = copilot.network_client("8080", log_level="error") assert client._actual_port == 8080 assert client._actual_host == "localhost" assert client._is_external_server def test_parse_host_port_url(self): - client = CopilotClient({"cli_url": "127.0.0.1:9000", "log_level": "error"}) + client = copilot.network_client("127.0.0.1:9000", log_level="error") assert client._actual_port == 9000 assert client._actual_host == "127.0.0.1" assert client._is_external_server def test_parse_http_url(self): - client = CopilotClient({"cli_url": "http://localhost:7000", "log_level": "error"}) + client = copilot.network_client("http://localhost:7000", log_level="error") assert client._actual_port == 7000 assert client._actual_host == "localhost" assert client._is_external_server def test_parse_https_url(self): - client = CopilotClient({"cli_url": "https://example.com:443", "log_level": "error"}) + client = copilot.network_client("https://example.com:443", log_level="error") assert client._actual_port == 443 assert client._actual_host == "example.com" assert client._is_external_server def test_invalid_url_format(self): with pytest.raises(ValueError, match="Invalid cli_url format"): - CopilotClient({"cli_url": "invalid-url", "log_level": "error"}) + copilot.network_client("invalid-url", log_level="error") def test_invalid_port_too_high(self): with pytest.raises(ValueError, match="Invalid port in cli_url"): - CopilotClient({"cli_url": "localhost:99999", "log_level": "error"}) + copilot.network_client("localhost:99999", log_level="error") def test_invalid_port_zero(self): with pytest.raises(ValueError, match="Invalid port in cli_url"): - CopilotClient({"cli_url": "localhost:0", "log_level": "error"}) + copilot.network_client("localhost:0", log_level="error") def test_invalid_port_negative(self): with pytest.raises(ValueError, match="Invalid port in cli_url"): - CopilotClient({"cli_url": "localhost:-1", "log_level": "error"}) + copilot.network_client("localhost:-1", log_level="error") - def test_cli_url_with_use_stdio(self): - with pytest.raises(ValueError, match="cli_url is mutually exclusive"): - CopilotClient({"cli_url": "localhost:8080", "use_stdio": True, "log_level": "error"}) - - def test_cli_url_with_cli_path(self): - with pytest.raises(ValueError, match="cli_url is mutually exclusive"): - CopilotClient( - {"cli_url": "localhost:8080", "cli_path": "/path/to/cli", "log_level": "error"} - ) - - def test_use_stdio_false_when_cli_url(self): - client = CopilotClient({"cli_url": "8080", "log_level": "error"}) - assert not client.options["use_stdio"] + def test_use_stdio_false_when_network(self): + client = copilot.network_client("8080", log_level="error") + assert not client._use_stdio def test_is_external_server_true(self): - client = CopilotClient({"cli_url": "localhost:8080", "log_level": "error"}) + client = copilot.network_client("localhost:8080", log_level="error") assert client._is_external_server class TestAuthOptions: def test_accepts_github_token(self): - client = CopilotClient( - {"cli_path": CLI_PATH, "github_token": "gho_test_token", "log_level": "error"} - ) - assert client.options.get("github_token") == "gho_test_token" + client = copilot.cli_client(CLI_PATH, github_token="gho_test_token", log_level="error") + assert client._github_token == "gho_test_token" def test_default_use_logged_in_user_true_without_token(self): - client = CopilotClient({"cli_path": CLI_PATH, "log_level": "error"}) - assert client.options.get("use_logged_in_user") is True + client = copilot.cli_client(CLI_PATH, log_level="error") + assert client._use_logged_in_user is True def test_default_use_logged_in_user_false_with_token(self): - client = CopilotClient( - {"cli_path": CLI_PATH, "github_token": "gho_test_token", "log_level": "error"} - ) - assert client.options.get("use_logged_in_user") is False + client = copilot.cli_client(CLI_PATH, github_token="gho_test_token", log_level="error") + assert client._use_logged_in_user is False def test_explicit_use_logged_in_user_true_with_token(self): - client = CopilotClient( - { - "cli_path": CLI_PATH, - "github_token": "gho_test_token", - "use_logged_in_user": True, - "log_level": "error", - } + client = copilot.cli_client( + CLI_PATH, + github_token="gho_test_token", + use_logged_in_user=True, + log_level="error", ) - assert client.options.get("use_logged_in_user") is True + assert client._use_logged_in_user is True def test_explicit_use_logged_in_user_false_without_token(self): - client = CopilotClient( - {"cli_path": CLI_PATH, "use_logged_in_user": False, "log_level": "error"} - ) - assert client.options.get("use_logged_in_user") is False - - def test_github_token_with_cli_url_raises(self): - with pytest.raises( - ValueError, match="github_token and use_logged_in_user cannot be used with cli_url" - ): - CopilotClient( - { - "cli_url": "localhost:8080", - "github_token": "gho_test_token", - "log_level": "error", - } - ) - - def test_use_logged_in_user_with_cli_url_raises(self): - with pytest.raises( - ValueError, match="github_token and use_logged_in_user cannot be used with cli_url" - ): - CopilotClient( - {"cli_url": "localhost:8080", "use_logged_in_user": False, "log_level": "error"} - ) + client = copilot.cli_client(CLI_PATH, use_logged_in_user=False, log_level="error") + assert client._use_logged_in_user is False class TestSessionConfigForwarding: @pytest.mark.asyncio async def test_create_session_forwards_client_name(self): - client = CopilotClient({"cli_path": CLI_PATH}) + client = copilot.cli_client(CLI_PATH) await client.start() try: @@ -200,7 +163,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_resume_session_forwards_client_name(self): - client = CopilotClient({"cli_path": CLI_PATH}) + client = copilot.cli_client(CLI_PATH) await client.start() try: diff --git a/test/scenarios/auth/byok-anthropic/python/main.py b/test/scenarios/auth/byok-anthropic/python/main.py index 7f5e5834c..a2f6106a7 100644 --- a/test/scenarios/auth/byok-anthropic/python/main.py +++ b/test/scenarios/auth/byok-anthropic/python/main.py @@ -1,7 +1,7 @@ import asyncio import os import sys -from copilot import CopilotClient +import copilot ANTHROPIC_API_KEY = os.environ.get("ANTHROPIC_API_KEY") ANTHROPIC_MODEL = os.environ.get("ANTHROPIC_MODEL", "claude-sonnet-4-20250514") @@ -13,10 +13,7 @@ async def main(): - opts = {} - if os.environ.get("COPILOT_CLI_PATH"): - opts["cli_path"] = os.environ["COPILOT_CLI_PATH"] - client = CopilotClient(opts) + client = copilot.cli_client(os.environ.get("COPILOT_CLI_PATH")) try: session = await client.create_session({ diff --git a/test/scenarios/auth/byok-azure/python/main.py b/test/scenarios/auth/byok-azure/python/main.py index 5376cac28..119bac29f 100644 --- a/test/scenarios/auth/byok-azure/python/main.py +++ b/test/scenarios/auth/byok-azure/python/main.py @@ -1,7 +1,7 @@ import asyncio import os import sys -from copilot import CopilotClient +import copilot AZURE_OPENAI_ENDPOINT = os.environ.get("AZURE_OPENAI_ENDPOINT") AZURE_OPENAI_API_KEY = os.environ.get("AZURE_OPENAI_API_KEY") @@ -14,10 +14,7 @@ async def main(): - opts = {} - if os.environ.get("COPILOT_CLI_PATH"): - opts["cli_path"] = os.environ["COPILOT_CLI_PATH"] - client = CopilotClient(opts) + client = copilot.cli_client(os.environ.get("COPILOT_CLI_PATH")) try: session = await client.create_session({ diff --git a/test/scenarios/auth/byok-ollama/python/main.py b/test/scenarios/auth/byok-ollama/python/main.py index 0f9df7f54..b787b7bb5 100644 --- a/test/scenarios/auth/byok-ollama/python/main.py +++ b/test/scenarios/auth/byok-ollama/python/main.py @@ -1,7 +1,7 @@ import asyncio import os import sys -from copilot import CopilotClient +import copilot OLLAMA_BASE_URL = os.environ.get("OLLAMA_BASE_URL", "http://localhost:11434/v1") OLLAMA_MODEL = os.environ.get("OLLAMA_MODEL", "llama3.2:3b") @@ -12,10 +12,7 @@ async def main(): - opts = {} - if os.environ.get("COPILOT_CLI_PATH"): - opts["cli_path"] = os.environ["COPILOT_CLI_PATH"] - client = CopilotClient(opts) + client = copilot.cli_client(os.environ.get("COPILOT_CLI_PATH")) try: session = await client.create_session({ diff --git a/test/scenarios/auth/byok-openai/python/main.py b/test/scenarios/auth/byok-openai/python/main.py index 651a92cd6..9e859da46 100644 --- a/test/scenarios/auth/byok-openai/python/main.py +++ b/test/scenarios/auth/byok-openai/python/main.py @@ -1,7 +1,7 @@ import asyncio import os import sys -from copilot import CopilotClient +import copilot OPENAI_BASE_URL = os.environ.get("OPENAI_BASE_URL", "https://api.openai.com/v1") OPENAI_MODEL = os.environ.get("OPENAI_MODEL", "claude-haiku-4.5") @@ -13,10 +13,7 @@ async def main(): - opts = {} - if os.environ.get("COPILOT_CLI_PATH"): - opts["cli_path"] = os.environ["COPILOT_CLI_PATH"] - client = CopilotClient(opts) + client = copilot.cli_client(os.environ.get("COPILOT_CLI_PATH")) try: session = await client.create_session({ diff --git a/test/scenarios/auth/gh-app/python/main.py b/test/scenarios/auth/gh-app/python/main.py index 4568c82b2..dd839685e 100644 --- a/test/scenarios/auth/gh-app/python/main.py +++ b/test/scenarios/auth/gh-app/python/main.py @@ -4,7 +4,7 @@ import time import urllib.request -from copilot import CopilotClient +import copilot DEVICE_CODE_URL = "https://github.com/login/device/code" @@ -78,10 +78,7 @@ async def main(): display_name = f" ({user.get('name')})" if user.get("name") else "" print(f"Authenticated as: {user.get('login')}{display_name}") - opts = {"github_token": token} - if os.environ.get("COPILOT_CLI_PATH"): - opts["cli_path"] = os.environ["COPILOT_CLI_PATH"] - client = CopilotClient(opts) + client = copilot.cli_client(os.environ.get("COPILOT_CLI_PATH"), github_token=token) try: session = await client.create_session({"model": "claude-haiku-4.5"}) diff --git a/test/scenarios/bundling/app-backend-to-server/python/main.py b/test/scenarios/bundling/app-backend-to-server/python/main.py index 218505f4a..4bba8d16c 100644 --- a/test/scenarios/bundling/app-backend-to-server/python/main.py +++ b/test/scenarios/bundling/app-backend-to-server/python/main.py @@ -5,7 +5,7 @@ import urllib.request from flask import Flask, request, jsonify -from copilot import CopilotClient +import copilot app = Flask(__name__) @@ -13,7 +13,7 @@ async def ask_copilot(prompt: str) -> str: - client = CopilotClient({"cli_url": CLI_URL}) + client = copilot.network_client(CLI_URL) try: session = await client.create_session({"model": "claude-haiku-4.5"}) diff --git a/test/scenarios/bundling/app-direct-server/python/main.py b/test/scenarios/bundling/app-direct-server/python/main.py index 05aaa9270..1232ebba6 100644 --- a/test/scenarios/bundling/app-direct-server/python/main.py +++ b/test/scenarios/bundling/app-direct-server/python/main.py @@ -1,12 +1,10 @@ import asyncio import os -from copilot import CopilotClient +import copilot async def main(): - client = CopilotClient({ - "cli_url": os.environ.get("COPILOT_CLI_URL", "localhost:3000"), - }) + client = copilot.network_client(os.environ.get("COPILOT_CLI_URL", "localhost:3000")) try: session = await client.create_session({"model": "claude-haiku-4.5"}) diff --git a/test/scenarios/bundling/container-proxy/python/main.py b/test/scenarios/bundling/container-proxy/python/main.py index 05aaa9270..1232ebba6 100644 --- a/test/scenarios/bundling/container-proxy/python/main.py +++ b/test/scenarios/bundling/container-proxy/python/main.py @@ -1,12 +1,10 @@ import asyncio import os -from copilot import CopilotClient +import copilot async def main(): - client = CopilotClient({ - "cli_url": os.environ.get("COPILOT_CLI_URL", "localhost:3000"), - }) + client = copilot.network_client(os.environ.get("COPILOT_CLI_URL", "localhost:3000")) try: session = await client.create_session({"model": "claude-haiku-4.5"}) diff --git a/test/scenarios/bundling/fully-bundled/python/main.py b/test/scenarios/bundling/fully-bundled/python/main.py index 138bb5646..7ecce366d 100644 --- a/test/scenarios/bundling/fully-bundled/python/main.py +++ b/test/scenarios/bundling/fully-bundled/python/main.py @@ -1,13 +1,10 @@ import asyncio import os -from copilot import CopilotClient +import copilot async def main(): - opts = {"github_token": os.environ.get("GITHUB_TOKEN")} - if os.environ.get("COPILOT_CLI_PATH"): - opts["cli_path"] = os.environ["COPILOT_CLI_PATH"] - client = CopilotClient(opts) + client = copilot.cli_client(os.environ.get("COPILOT_CLI_PATH"), github_token=os.environ.get("GITHUB_TOKEN")) try: session = await client.create_session({"model": "claude-haiku-4.5"}) diff --git a/test/scenarios/callbacks/hooks/python/main.py b/test/scenarios/callbacks/hooks/python/main.py index a00c18af7..f79ff2498 100644 --- a/test/scenarios/callbacks/hooks/python/main.py +++ b/test/scenarios/callbacks/hooks/python/main.py @@ -1,6 +1,6 @@ import asyncio import os -from copilot import CopilotClient +import copilot hook_log: list[str] = [] @@ -40,10 +40,7 @@ async def on_error_occurred(input_data, invocation): async def main(): - opts = {"github_token": os.environ.get("GITHUB_TOKEN")} - if os.environ.get("COPILOT_CLI_PATH"): - opts["cli_path"] = os.environ["COPILOT_CLI_PATH"] - client = CopilotClient(opts) + client = copilot.cli_client(os.environ.get("COPILOT_CLI_PATH"), github_token=os.environ.get("GITHUB_TOKEN")) try: session = await client.create_session( diff --git a/test/scenarios/callbacks/permissions/python/main.py b/test/scenarios/callbacks/permissions/python/main.py index 2da5133fa..7c26f80a8 100644 --- a/test/scenarios/callbacks/permissions/python/main.py +++ b/test/scenarios/callbacks/permissions/python/main.py @@ -1,6 +1,6 @@ import asyncio import os -from copilot import CopilotClient +import copilot # Track which tools requested permission permission_log: list[str] = [] @@ -16,10 +16,7 @@ async def auto_approve_tool(input_data, invocation): async def main(): - opts = {"github_token": os.environ.get("GITHUB_TOKEN")} - if os.environ.get("COPILOT_CLI_PATH"): - opts["cli_path"] = os.environ["COPILOT_CLI_PATH"] - client = CopilotClient(opts) + client = copilot.cli_client(os.environ.get("COPILOT_CLI_PATH"), github_token=os.environ.get("GITHUB_TOKEN")) try: session = await client.create_session( diff --git a/test/scenarios/callbacks/user-input/python/main.py b/test/scenarios/callbacks/user-input/python/main.py index fb36eda5c..8fef6b2f4 100644 --- a/test/scenarios/callbacks/user-input/python/main.py +++ b/test/scenarios/callbacks/user-input/python/main.py @@ -1,6 +1,6 @@ import asyncio import os -from copilot import CopilotClient +import copilot input_log: list[str] = [] @@ -20,10 +20,7 @@ async def handle_user_input(request, invocation): async def main(): - opts = {"github_token": os.environ.get("GITHUB_TOKEN")} - if os.environ.get("COPILOT_CLI_PATH"): - opts["cli_path"] = os.environ["COPILOT_CLI_PATH"] - client = CopilotClient(opts) + client = copilot.cli_client(os.environ.get("COPILOT_CLI_PATH"), github_token=os.environ.get("GITHUB_TOKEN")) try: session = await client.create_session( diff --git a/test/scenarios/modes/default/python/main.py b/test/scenarios/modes/default/python/main.py index 0abc6b709..b21fa8e4d 100644 --- a/test/scenarios/modes/default/python/main.py +++ b/test/scenarios/modes/default/python/main.py @@ -1,13 +1,10 @@ import asyncio import os -from copilot import CopilotClient +import copilot async def main(): - opts = {"github_token": os.environ.get("GITHUB_TOKEN")} - if os.environ.get("COPILOT_CLI_PATH"): - opts["cli_path"] = os.environ["COPILOT_CLI_PATH"] - client = CopilotClient(opts) + client = copilot.cli_client(os.environ.get("COPILOT_CLI_PATH"), github_token=os.environ.get("GITHUB_TOKEN")) try: session = await client.create_session({ diff --git a/test/scenarios/modes/minimal/python/main.py b/test/scenarios/modes/minimal/python/main.py index 74a98ba0e..1b6bfc2be 100644 --- a/test/scenarios/modes/minimal/python/main.py +++ b/test/scenarios/modes/minimal/python/main.py @@ -1,13 +1,10 @@ import asyncio import os -from copilot import CopilotClient +import copilot async def main(): - opts = {"github_token": os.environ.get("GITHUB_TOKEN")} - if os.environ.get("COPILOT_CLI_PATH"): - opts["cli_path"] = os.environ["COPILOT_CLI_PATH"] - client = CopilotClient(opts) + client = copilot.cli_client(os.environ.get("COPILOT_CLI_PATH"), github_token=os.environ.get("GITHUB_TOKEN")) try: session = await client.create_session({ diff --git a/test/scenarios/prompts/attachments/python/main.py b/test/scenarios/prompts/attachments/python/main.py index acf9c7af1..7cbc00ba4 100644 --- a/test/scenarios/prompts/attachments/python/main.py +++ b/test/scenarios/prompts/attachments/python/main.py @@ -1,15 +1,12 @@ import asyncio import os -from copilot import CopilotClient +import copilot SYSTEM_PROMPT = """You are a helpful assistant. Answer questions about attached files concisely.""" async def main(): - opts = {"github_token": os.environ.get("GITHUB_TOKEN")} - if os.environ.get("COPILOT_CLI_PATH"): - opts["cli_path"] = os.environ["COPILOT_CLI_PATH"] - client = CopilotClient(opts) + client = copilot.cli_client(os.environ.get("COPILOT_CLI_PATH"), github_token=os.environ.get("GITHUB_TOKEN")) try: session = await client.create_session( diff --git a/test/scenarios/prompts/reasoning-effort/python/main.py b/test/scenarios/prompts/reasoning-effort/python/main.py index 74444e7bf..775381090 100644 --- a/test/scenarios/prompts/reasoning-effort/python/main.py +++ b/test/scenarios/prompts/reasoning-effort/python/main.py @@ -1,13 +1,10 @@ import asyncio import os -from copilot import CopilotClient +import copilot async def main(): - opts = {"github_token": os.environ.get("GITHUB_TOKEN")} - if os.environ.get("COPILOT_CLI_PATH"): - opts["cli_path"] = os.environ["COPILOT_CLI_PATH"] - client = CopilotClient(opts) + client = copilot.cli_client(os.environ.get("COPILOT_CLI_PATH"), github_token=os.environ.get("GITHUB_TOKEN")) try: session = await client.create_session({ diff --git a/test/scenarios/prompts/system-message/python/main.py b/test/scenarios/prompts/system-message/python/main.py index a3bfccdcf..5612b4980 100644 --- a/test/scenarios/prompts/system-message/python/main.py +++ b/test/scenarios/prompts/system-message/python/main.py @@ -1,15 +1,12 @@ import asyncio import os -from copilot import CopilotClient +import copilot PIRATE_PROMPT = """You are a pirate. Always respond in pirate speak. Say 'Arrr!' in every response. Use nautical terms and pirate slang throughout.""" async def main(): - opts = {"github_token": os.environ.get("GITHUB_TOKEN")} - if os.environ.get("COPILOT_CLI_PATH"): - opts["cli_path"] = os.environ["COPILOT_CLI_PATH"] - client = CopilotClient(opts) + client = copilot.cli_client(os.environ.get("COPILOT_CLI_PATH"), github_token=os.environ.get("GITHUB_TOKEN")) try: session = await client.create_session( diff --git a/test/scenarios/sessions/concurrent-sessions/python/main.py b/test/scenarios/sessions/concurrent-sessions/python/main.py index 171a202e4..62cb025b5 100644 --- a/test/scenarios/sessions/concurrent-sessions/python/main.py +++ b/test/scenarios/sessions/concurrent-sessions/python/main.py @@ -1,16 +1,13 @@ import asyncio import os -from copilot import CopilotClient +import copilot PIRATE_PROMPT = "You are a pirate. Always say Arrr!" ROBOT_PROMPT = "You are a robot. Always say BEEP BOOP!" async def main(): - opts = {"github_token": os.environ.get("GITHUB_TOKEN")} - if os.environ.get("COPILOT_CLI_PATH"): - opts["cli_path"] = os.environ["COPILOT_CLI_PATH"] - client = CopilotClient(opts) + client = copilot.cli_client(os.environ.get("COPILOT_CLI_PATH"), github_token=os.environ.get("GITHUB_TOKEN")) try: session1, session2 = await asyncio.gather( diff --git a/test/scenarios/sessions/infinite-sessions/python/main.py b/test/scenarios/sessions/infinite-sessions/python/main.py index fe39a7117..499d68f23 100644 --- a/test/scenarios/sessions/infinite-sessions/python/main.py +++ b/test/scenarios/sessions/infinite-sessions/python/main.py @@ -1,13 +1,10 @@ import asyncio import os -from copilot import CopilotClient +import copilot async def main(): - opts = {"github_token": os.environ.get("GITHUB_TOKEN")} - if os.environ.get("COPILOT_CLI_PATH"): - opts["cli_path"] = os.environ["COPILOT_CLI_PATH"] - client = CopilotClient(opts) + client = copilot.cli_client(os.environ.get("COPILOT_CLI_PATH"), github_token=os.environ.get("GITHUB_TOKEN")) try: session = await client.create_session({ diff --git a/test/scenarios/sessions/session-resume/python/main.py b/test/scenarios/sessions/session-resume/python/main.py index b65370b97..d44336af2 100644 --- a/test/scenarios/sessions/session-resume/python/main.py +++ b/test/scenarios/sessions/session-resume/python/main.py @@ -1,13 +1,10 @@ import asyncio import os -from copilot import CopilotClient +import copilot async def main(): - opts = {"github_token": os.environ.get("GITHUB_TOKEN")} - if os.environ.get("COPILOT_CLI_PATH"): - opts["cli_path"] = os.environ["COPILOT_CLI_PATH"] - client = CopilotClient(opts) + client = copilot.cli_client(os.environ.get("COPILOT_CLI_PATH"), github_token=os.environ.get("GITHUB_TOKEN")) try: # 1. Create a session diff --git a/test/scenarios/sessions/streaming/python/main.py b/test/scenarios/sessions/streaming/python/main.py index 2bbc94e78..6acaae6f9 100644 --- a/test/scenarios/sessions/streaming/python/main.py +++ b/test/scenarios/sessions/streaming/python/main.py @@ -1,13 +1,10 @@ import asyncio import os -from copilot import CopilotClient +import copilot async def main(): - opts = {"github_token": os.environ.get("GITHUB_TOKEN")} - if os.environ.get("COPILOT_CLI_PATH"): - opts["cli_path"] = os.environ["COPILOT_CLI_PATH"] - client = CopilotClient(opts) + client = copilot.cli_client(os.environ.get("COPILOT_CLI_PATH"), github_token=os.environ.get("GITHUB_TOKEN")) try: session = await client.create_session( diff --git a/test/scenarios/tools/custom-agents/python/main.py b/test/scenarios/tools/custom-agents/python/main.py index d4e416716..6e4167eeb 100644 --- a/test/scenarios/tools/custom-agents/python/main.py +++ b/test/scenarios/tools/custom-agents/python/main.py @@ -1,13 +1,10 @@ import asyncio import os -from copilot import CopilotClient +import copilot async def main(): - opts = {"github_token": os.environ.get("GITHUB_TOKEN")} - if os.environ.get("COPILOT_CLI_PATH"): - opts["cli_path"] = os.environ["COPILOT_CLI_PATH"] - client = CopilotClient(opts) + client = copilot.cli_client(os.environ.get("COPILOT_CLI_PATH"), github_token=os.environ.get("GITHUB_TOKEN")) try: session = await client.create_session( diff --git a/test/scenarios/tools/mcp-servers/python/main.py b/test/scenarios/tools/mcp-servers/python/main.py index 81d2e39ba..eb0ab4921 100644 --- a/test/scenarios/tools/mcp-servers/python/main.py +++ b/test/scenarios/tools/mcp-servers/python/main.py @@ -1,13 +1,10 @@ import asyncio import os -from copilot import CopilotClient +import copilot async def main(): - opts = {"github_token": os.environ.get("GITHUB_TOKEN")} - if os.environ.get("COPILOT_CLI_PATH"): - opts["cli_path"] = os.environ["COPILOT_CLI_PATH"] - client = CopilotClient(opts) + client = copilot.cli_client(os.environ.get("COPILOT_CLI_PATH"), github_token=os.environ.get("GITHUB_TOKEN")) try: # MCP server config — demonstrates the configuration pattern. diff --git a/test/scenarios/tools/no-tools/python/main.py b/test/scenarios/tools/no-tools/python/main.py index d857183c0..83b8a3e11 100644 --- a/test/scenarios/tools/no-tools/python/main.py +++ b/test/scenarios/tools/no-tools/python/main.py @@ -1,6 +1,6 @@ import asyncio import os -from copilot import CopilotClient +import copilot SYSTEM_PROMPT = """You are a minimal assistant with no tools available. You cannot execute code, read files, edit files, search, or perform any actions. @@ -9,10 +9,7 @@ async def main(): - opts = {"github_token": os.environ.get("GITHUB_TOKEN")} - if os.environ.get("COPILOT_CLI_PATH"): - opts["cli_path"] = os.environ["COPILOT_CLI_PATH"] - client = CopilotClient(opts) + client = copilot.cli_client(os.environ.get("COPILOT_CLI_PATH"), github_token=os.environ.get("GITHUB_TOKEN")) try: session = await client.create_session( diff --git a/test/scenarios/tools/skills/python/main.py b/test/scenarios/tools/skills/python/main.py index 5adb74b76..a9f3e99a0 100644 --- a/test/scenarios/tools/skills/python/main.py +++ b/test/scenarios/tools/skills/python/main.py @@ -2,14 +2,11 @@ import os from pathlib import Path -from copilot import CopilotClient +import copilot async def main(): - opts = {"github_token": os.environ.get("GITHUB_TOKEN")} - if os.environ.get("COPILOT_CLI_PATH"): - opts["cli_path"] = os.environ["COPILOT_CLI_PATH"] - client = CopilotClient(opts) + client = copilot.cli_client(os.environ.get("COPILOT_CLI_PATH"), github_token=os.environ.get("GITHUB_TOKEN")) try: skills_dir = str(Path(__file__).resolve().parent.parent / "sample-skills") diff --git a/test/scenarios/tools/tool-filtering/python/main.py b/test/scenarios/tools/tool-filtering/python/main.py index 174be620e..808194eed 100644 --- a/test/scenarios/tools/tool-filtering/python/main.py +++ b/test/scenarios/tools/tool-filtering/python/main.py @@ -1,15 +1,12 @@ import asyncio import os -from copilot import CopilotClient +import copilot SYSTEM_PROMPT = """You are a helpful assistant. You have access to a limited set of tools. When asked about your tools, list exactly which tools you have available.""" async def main(): - opts = {"github_token": os.environ.get("GITHUB_TOKEN")} - if os.environ.get("COPILOT_CLI_PATH"): - opts["cli_path"] = os.environ["COPILOT_CLI_PATH"] - client = CopilotClient(opts) + client = copilot.cli_client(os.environ.get("COPILOT_CLI_PATH"), github_token=os.environ.get("GITHUB_TOKEN")) try: session = await client.create_session( diff --git a/test/scenarios/tools/virtual-filesystem/python/main.py b/test/scenarios/tools/virtual-filesystem/python/main.py index b150c1a2a..503371c04 100644 --- a/test/scenarios/tools/virtual-filesystem/python/main.py +++ b/test/scenarios/tools/virtual-filesystem/python/main.py @@ -1,6 +1,7 @@ import asyncio import os -from copilot import CopilotClient, define_tool +import copilot +from copilot import define_tool from pydantic import BaseModel, Field # In-memory virtual filesystem @@ -46,10 +47,7 @@ async def auto_approve_tool(input_data, invocation): async def main(): - opts = {"github_token": os.environ.get("GITHUB_TOKEN")} - if os.environ.get("COPILOT_CLI_PATH"): - opts["cli_path"] = os.environ["COPILOT_CLI_PATH"] - client = CopilotClient(opts) + client = copilot.cli_client(os.environ.get("COPILOT_CLI_PATH"), github_token=os.environ.get("GITHUB_TOKEN")) try: session = await client.create_session( diff --git a/test/scenarios/transport/reconnect/python/main.py b/test/scenarios/transport/reconnect/python/main.py index e8aecea50..90f548ff2 100644 --- a/test/scenarios/transport/reconnect/python/main.py +++ b/test/scenarios/transport/reconnect/python/main.py @@ -1,13 +1,11 @@ import asyncio import os import sys -from copilot import CopilotClient +import copilot async def main(): - client = CopilotClient({ - "cli_url": os.environ.get("COPILOT_CLI_URL", "localhost:3000"), - }) + client = copilot.network_client(os.environ.get("COPILOT_CLI_URL", "localhost:3000")) try: # First session diff --git a/test/scenarios/transport/stdio/python/main.py b/test/scenarios/transport/stdio/python/main.py index 138bb5646..7ecce366d 100644 --- a/test/scenarios/transport/stdio/python/main.py +++ b/test/scenarios/transport/stdio/python/main.py @@ -1,13 +1,10 @@ import asyncio import os -from copilot import CopilotClient +import copilot async def main(): - opts = {"github_token": os.environ.get("GITHUB_TOKEN")} - if os.environ.get("COPILOT_CLI_PATH"): - opts["cli_path"] = os.environ["COPILOT_CLI_PATH"] - client = CopilotClient(opts) + client = copilot.cli_client(os.environ.get("COPILOT_CLI_PATH"), github_token=os.environ.get("GITHUB_TOKEN")) try: session = await client.create_session({"model": "claude-haiku-4.5"}) diff --git a/test/scenarios/transport/tcp/python/main.py b/test/scenarios/transport/tcp/python/main.py index 05aaa9270..1232ebba6 100644 --- a/test/scenarios/transport/tcp/python/main.py +++ b/test/scenarios/transport/tcp/python/main.py @@ -1,12 +1,10 @@ import asyncio import os -from copilot import CopilotClient +import copilot async def main(): - client = CopilotClient({ - "cli_url": os.environ.get("COPILOT_CLI_URL", "localhost:3000"), - }) + client = copilot.network_client(os.environ.get("COPILOT_CLI_URL", "localhost:3000")) try: session = await client.create_session({"model": "claude-haiku-4.5"})