CopilotClientOptions.java
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------------------------------------------*/
package com.github.copilot.sdk.json;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
import com.fasterxml.jackson.annotation.JsonInclude;
/**
* Configuration options for creating a
* {@link com.github.copilot.sdk.CopilotClient}.
* <p>
* This class provides a fluent API for configuring how the client connects to
* and manages the Copilot CLI server. All setter methods return {@code this}
* for method chaining.
*
* <h2>Example Usage</h2>
*
* <pre>{@code
* var options = new CopilotClientOptions().setCliPath("/usr/local/bin/copilot").setLogLevel("debug")
* .setAutoStart(true);
*
* var client = new CopilotClient(options);
* }</pre>
*
* @see com.github.copilot.sdk.CopilotClient
* @since 1.0.0
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class CopilotClientOptions {
@Deprecated
private boolean autoRestart;
private boolean autoStart = true;
private String[] cliArgs;
private String cliPath;
private String cliUrl;
private String cwd;
private Map<String, String> environment;
private Executor executor;
private String gitHubToken;
private String logLevel = "info";
private Supplier<CompletableFuture<List<ModelInfo>>> onListModels;
private int port;
private TelemetryConfig telemetry;
private Boolean useLoggedInUser;
private boolean useStdio = true;
/**
* Returns whether the client should automatically restart the server on crash.
*
* @return the auto-restart flag value (no longer has any effect)
* @deprecated This option has no effect and will be removed in a future
* release.
*/
@Deprecated
public boolean isAutoRestart() {
return autoRestart;
}
/**
* Sets whether the client should automatically restart the CLI server if it
* crashes unexpectedly.
*
* @param autoRestart
* ignored — this option no longer has any effect
* @return this options instance for method chaining
* @deprecated This option has no effect and will be removed in a future
* release.
*/
@Deprecated
public CopilotClientOptions setAutoRestart(boolean autoRestart) {
this.autoRestart = autoRestart;
return this;
}
/**
* Returns whether the client should automatically start the server.
*
* @return {@code true} to auto-start (default), {@code false} for manual start
*/
public boolean isAutoStart() {
return autoStart;
}
/**
* Sets whether the client should automatically start the CLI server when the
* first request is made.
*
* @param autoStart
* {@code true} to auto-start, {@code false} for manual start
* @return this options instance for method chaining
*/
public CopilotClientOptions setAutoStart(boolean autoStart) {
this.autoStart = autoStart;
return this;
}
/**
* Gets the extra CLI arguments.
* <p>
* Returns a shallow copy of the internal array, or {@code null} if no arguments
* have been set.
*
* @return a copy of the extra arguments, or {@code null}
*/
public String[] getCliArgs() {
return cliArgs != null ? Arrays.copyOf(cliArgs, cliArgs.length) : null;
}
/**
* Sets extra arguments to pass to the CLI process.
* <p>
* These arguments are prepended before SDK-managed flags. A shallow copy of the
* provided array is stored. If {@code null} or empty, the existing arguments
* are cleared.
*
* @param cliArgs
* the extra arguments to pass, or {@code null}/empty to clear
* @return this options instance for method chaining
*/
public CopilotClientOptions setCliArgs(String[] cliArgs) {
if (cliArgs == null || cliArgs.length == 0) {
if (this.cliArgs != null) {
this.cliArgs = new String[0];
}
} else {
this.cliArgs = Arrays.copyOf(cliArgs, cliArgs.length);
}
return this;
}
/**
* Gets the path to the Copilot CLI executable.
*
* @return the CLI path, or {@code null} to use "copilot" from PATH
*/
public String getCliPath() {
return cliPath;
}
/**
* Sets the path to the Copilot CLI executable.
*
* @param cliPath
* the path to the CLI executable
* @return this options instance for method chaining
*/
public CopilotClientOptions setCliPath(String cliPath) {
this.cliPath = Objects.requireNonNull(cliPath, "cliPath must not be null");
return this;
}
/**
* Gets the URL of an existing CLI server to connect to.
*
* @return the CLI server URL, or {@code null} to spawn a new process
*/
public String getCliUrl() {
return cliUrl;
}
/**
* Sets the URL of an existing CLI server to connect to.
* <p>
* When provided, the client will not spawn a CLI process but will connect to
* the specified URL instead. Format: "host:port" or "http://host:port".
* <p>
* <strong>Note:</strong> This is mutually exclusive with
* {@link #setUseStdio(boolean)} and {@link #setCliPath(String)}.
*
* @param cliUrl
* the CLI server URL to connect to (must not be {@code null} or
* empty)
* @return this options instance for method chaining
* @throws IllegalArgumentException
* if {@code cliUrl} is {@code null} or empty
*/
public CopilotClientOptions setCliUrl(String cliUrl) {
this.cliUrl = Objects.requireNonNull(cliUrl, "cliUrl must not be null");
return this;
}
/**
* Gets the working directory for the CLI process.
*
* @return the working directory path
*/
public String getCwd() {
return cwd;
}
/**
* Sets the working directory for the CLI process.
*
* @param cwd
* the working directory path (must not be {@code null} or empty)
* @return this options instance for method chaining
* @throws IllegalArgumentException
* if {@code cwd} is {@code null} or empty
*/
public CopilotClientOptions setCwd(String cwd) {
this.cwd = Objects.requireNonNull(cwd, "cwd must not be null");
return this;
}
/**
* Gets the environment variables for the CLI process.
* <p>
* Returns a shallow copy of the internal map, or {@code null} if no environment
* has been set.
*
* @return a copy of the environment variables map, or {@code null}
*/
public Map<String, String> getEnvironment() {
return environment != null ? new HashMap<>(environment) : null;
}
/**
* Sets environment variables to pass to the CLI process.
* <p>
* When set, these environment variables replace the inherited environment. A
* shallow copy of the provided map is stored. If {@code null} or empty, the
* existing environment is cleared.
*
* @param environment
* the environment variables map, or {@code null}/empty to clear
* @return this options instance for method chaining
*/
public CopilotClientOptions setEnvironment(Map<String, String> environment) {
if (environment == null || environment.isEmpty()) {
if (this.environment != null) {
this.environment.clear();
}
} else {
this.environment = new HashMap<>(environment);
}
return this;
}
/**
* Gets the executor used for internal asynchronous operations.
*
* @return the executor, or {@code null} to use the default
* {@code ForkJoinPool.commonPool()}
*/
public Executor getExecutor() {
return executor;
}
/**
* Sets the executor used for internal asynchronous operations.
* <p>
* When provided, the SDK uses this executor for all internal
* {@code CompletableFuture} combinators instead of the default
* {@code ForkJoinPool.commonPool()}. This allows callers to isolate SDK work
* onto a dedicated thread pool or integrate with container-managed threading.
* <p>
* Passing {@code null} reverts to the default {@code ForkJoinPool.commonPool()}
* behavior.
*
* @param executor
* the executor to use, or {@code null} for the default
* @return this options instance for fluent chaining
*/
public CopilotClientOptions setExecutor(Executor executor) {
this.executor = executor;
return this;
}
/**
* Gets the GitHub token for authentication.
*
* @return the GitHub token, or {@code null} to use other authentication methods
*/
public String getGitHubToken() {
return gitHubToken;
}
/**
* Sets the GitHub token to use for authentication.
* <p>
* When provided, the token is passed to the CLI server via environment
* variable. This takes priority over other authentication methods.
*
* @param gitHubToken
* the GitHub token (must not be {@code null} or empty)
* @return this options instance for method chaining
* @throws IllegalArgumentException
* if {@code gitHubToken} is {@code null} or empty
*/
public CopilotClientOptions setGitHubToken(String gitHubToken) {
this.gitHubToken = Objects.requireNonNull(gitHubToken, "gitHubToken must not be null");
return this;
}
/**
* Gets the GitHub token for authentication.
*
* @return the GitHub token, or {@code null} to use other authentication methods
* @deprecated Use {@link #getGitHubToken()} instead.
*/
@Deprecated
public String getGithubToken() {
return gitHubToken;
}
/**
* Sets the GitHub token to use for authentication.
*
* @param githubToken
* the GitHub token
* @return this options instance for method chaining
* @deprecated Use {@link #setGitHubToken(String)} instead.
*/
@Deprecated
public CopilotClientOptions setGithubToken(String githubToken) {
this.gitHubToken = Objects.requireNonNull(githubToken, "githubToken must not be null");
return this;
}
/**
* Gets the log level for the CLI process.
*
* @return the log level (default: "info")
*/
public String getLogLevel() {
return logLevel;
}
/**
* Sets the log level for the CLI process.
* <p>
* Valid levels include: "error", "warn", "info", "debug", "trace".
*
* @param logLevel
* the log level (must not be {@code null} or empty)
* @return this options instance for method chaining
* @throws IllegalArgumentException
* if {@code logLevel} is {@code null} or empty
*/
public CopilotClientOptions setLogLevel(String logLevel) {
this.logLevel = Objects.requireNonNull(logLevel, "logLevel must not be null");
return this;
}
/**
* Gets the custom handler for listing available models.
*
* @return the handler, or {@code null} if not set
*/
public Supplier<CompletableFuture<List<ModelInfo>>> getOnListModels() {
return onListModels;
}
/**
* Sets a custom handler for listing available models.
* <p>
* When provided, {@code listModels()} calls this handler instead of querying
* the CLI server. Useful in BYOK (Bring Your Own Key) mode to return models
* available from your custom provider.
*
* @param onListModels
* the handler that returns the list of available models (must not be
* {@code null})
* @return this options instance for method chaining
* @throws IllegalArgumentException
* if {@code onListModels} is {@code null}
*/
public CopilotClientOptions setOnListModels(Supplier<CompletableFuture<List<ModelInfo>>> onListModels) {
this.onListModels = Objects.requireNonNull(onListModels, "onListModels must not be null");
return this;
}
/**
* Gets the TCP port for the CLI server.
*
* @return the port number, or 0 for a random port
*/
public int getPort() {
return port;
}
/**
* Sets the TCP port for the CLI server to listen on.
* <p>
* This is only used when {@link #isUseStdio()} is {@code false}.
*
* @param port
* the port number, or 0 for a random port
* @return this options instance for method chaining
*/
public CopilotClientOptions setPort(int port) {
this.port = port;
return this;
}
/**
* Gets the OpenTelemetry configuration for the CLI server.
*
* @return the telemetry config, or {@code null}
* @since 1.2.0
*/
public TelemetryConfig getTelemetry() {
return telemetry;
}
/**
* Sets the OpenTelemetry configuration for the CLI server.
* <p>
* When set, the CLI server is started with OpenTelemetry instrumentation
* enabled using the provided settings.
*
* @param telemetry
* the telemetry configuration
* @return this options instance for method chaining
* @since 1.2.0
*/
public CopilotClientOptions setTelemetry(TelemetryConfig telemetry) {
this.telemetry = Objects.requireNonNull(telemetry, "telemetry must not be null");
return this;
}
/**
* Returns whether to use the logged-in user for authentication.
*
* @return {@code true} to use logged-in user auth, {@code false} to use only
* explicit tokens, or {@code null} to use default behavior
*/
public Boolean getUseLoggedInUser() {
return useLoggedInUser;
}
/**
* Sets whether to use the logged-in user for authentication.
* <p>
* When true, the CLI server will attempt to use stored OAuth tokens or gh CLI
* auth. When false, only explicit tokens (gitHubToken or environment variables)
* are used. Default: true (but defaults to false when gitHubToken is provided).
* <p>
* Passing {@code null} is equivalent to passing {@link Boolean#FALSE}.
*
* @param useLoggedInUser
* {@code true} to use logged-in user auth, {@code false} or
* {@code null} otherwise
* @return this options instance for method chaining
*/
public CopilotClientOptions setUseLoggedInUser(Boolean useLoggedInUser) {
this.useLoggedInUser = useLoggedInUser != null ? useLoggedInUser : Boolean.FALSE;
return this;
}
/**
* Returns whether to use stdio transport instead of TCP.
*
* @return {@code true} to use stdio (default), {@code false} to use TCP
*/
public boolean isUseStdio() {
return useStdio;
}
/**
* Sets whether to use stdio transport instead of TCP.
* <p>
* Stdio transport is more efficient and is the default. TCP transport can be
* useful for debugging or connecting to remote servers.
*
* @param useStdio
* {@code true} to use stdio, {@code false} to use TCP
* @return this options instance for method chaining
*/
public CopilotClientOptions setUseStdio(boolean useStdio) {
this.useStdio = useStdio;
return this;
}
/**
* Creates a shallow clone of this {@code CopilotClientOptions} instance.
* <p>
* Array properties (like {@code cliArgs}) are copied into new arrays so that
* modifications to the clone do not affect the original. The
* {@code environment} map is also copied to a new map instance. Other
* reference-type properties are shared between the original and clone.
*
* @return a clone of this options instance
*/
@Override
public CopilotClientOptions clone() {
CopilotClientOptions copy = new CopilotClientOptions();
copy.autoRestart = this.autoRestart;
copy.autoStart = this.autoStart;
copy.cliArgs = this.cliArgs != null ? this.cliArgs.clone() : null;
copy.cliPath = this.cliPath;
copy.cliUrl = this.cliUrl;
copy.cwd = this.cwd;
copy.environment = this.environment != null ? new java.util.HashMap<>(this.environment) : null;
copy.executor = this.executor;
copy.gitHubToken = this.gitHubToken;
copy.logLevel = this.logLevel;
copy.onListModels = this.onListModels;
copy.port = this.port;
copy.telemetry = this.telemetry;
copy.useLoggedInUser = this.useLoggedInUser;
copy.useStdio = this.useStdio;
return copy;
}
}