/*--------------------------------------------------------------------------------------------- * 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}. *

* 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. * *

Example Usage

* *
{@code
 * var options = new CopilotClientOptions().setCliPath("/usr/local/bin/copilot").setLogLevel("debug")
 * 		.setAutoStart(true);
 *
 * var client = new CopilotClient(options);
 * }
* * @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 environment; private Executor executor; private String gitHubToken; private String logLevel = "info"; private Supplier>> 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. *

* 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. *

* 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. *

* 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". *

* Note: 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. *

* 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 getEnvironment() { return environment != null ? new HashMap<>(environment) : null; } /** * Sets environment variables to pass to the CLI process. *

* 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 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. *

* 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. *

* 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. *

* 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. *

* 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>> getOnListModels() { return onListModels; } /** * Sets a custom handler for listing available models. *

* 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>> 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. *

* 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. *

* 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. *

* 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). *

* 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. *

* 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. *

* 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; } }