Liking cljdoc? Tell your friends :D

Bundled CLI Setup

Package the Copilot CLI alongside your application so users don't need to install or configure anything separately. Your app ships with everything it needs.

Best for: Desktop apps, standalone tools, Electron apps, distributable CLI utilities.

How It Works

Instead of relying on a globally installed CLI, you include the CLI binary in your application bundle. The SDK points to your bundled copy via the cliPath option.

flowchart TB
    subgraph Bundle["Your Distributed App"]
        App["Application Code"]
        SDK["SDK Client"]
        CLIBin["Copilot CLI Binary<br/>(bundled)"]
    end

    App --> SDK
    SDK -- "cliPath" --> CLIBin
    CLIBin -- "API calls" --> Copilot["☁️ GitHub Copilot"]

    style Bundle fill:#0d1117,stroke:#58a6ff,color:#c9d1d9

Key characteristics:

  • CLI binary ships with your app — no separate install needed
  • You control the exact CLI version your app uses
  • Users authenticate through your app (or use env vars / BYOK)
  • Sessions are managed per-user on their machine

Architecture: Bundled vs. Installed

flowchart LR
    subgraph Installed["Standard Setup"]
        A1["Your App"] --> SDK1["SDK"]
        SDK1 --> CLI1["Global CLI<br/>(/usr/local/bin/copilot)"]
    end

    subgraph Bundled["Bundled Setup"]
        A2["Your App"] --> SDK2["SDK"]
        SDK2 --> CLI2["Bundled CLI<br/>(./vendor/copilot)"]
    end

    style Installed fill:#161b22,stroke:#8b949e,color:#c9d1d9
    style Bundled fill:#0d1117,stroke:#3fb950,color:#c9d1d9

Setup

1. Include the CLI in Your Project

The CLI is distributed as part of the @github/copilot npm package. You can also obtain platform-specific binaries for your distribution pipeline.

# The CLI is available from the @github/copilot package
npm install @github/copilot

2. Point the SDK to Your Bundled CLI

Node.js / TypeScript
import { CopilotClient } from "copilot-sdk-supercharged";
import path from "path";

const client = new CopilotClient({
    // Point to the CLI binary in your app bundle
    cliPath: path.join(__dirname, "vendor", "copilot"),
});

const session = await client.createSession({ model: "gpt-4.1" });
const response = await session.sendAndWait({ prompt: "Hello!" });
console.log(response?.data.content);

await client.stop();
Python
from copilot import CopilotClient
from pathlib import Path

client = CopilotClient({
    "cli_path": str(Path(__file__).parent / "vendor" / "copilot"),
})
await client.start()

session = await client.create_session({"model": "gpt-4.1"})
response = await session.send_and_wait({"prompt": "Hello!"})
print(response.data.content)

await client.stop()
Go
client := copilot.NewClient(&copilot.ClientOptions{
    CLIPath: "./vendor/copilot",
})
if err := client.Start(ctx); err != nil {
    log.Fatal(err)
}
defer client.Stop()

session, _ := client.CreateSession(ctx, &copilot.SessionConfig{Model: "gpt-4.1"})
response, _ := session.SendAndWait(ctx, copilot.MessageOptions{Prompt: "Hello!"})
fmt.Println(*response.Data.Content)
.NET
var client = new CopilotClient(new CopilotClientOptions
{
    CliPath = Path.Combine(AppContext.BaseDirectory, "vendor", "copilot"),
});

await using var session = await client.CreateSessionAsync(
    new SessionConfig { Model = "gpt-4.1" });

var response = await session.SendAndWaitAsync(
    new MessageOptions { Prompt = "Hello!" });
Console.WriteLine(response?.Data.Content);

Authentication Strategies

When bundling, you need to decide how your users will authenticate. Here are the common patterns:

flowchart TB
    App["Bundled App"]

    App --> A["User signs in to CLI<br/>(keychain credentials)"]
    App --> B["App provides token<br/>(OAuth / env var)"]
    App --> C["BYOK<br/>(your own API keys)"]

    A --> Note1["User runs 'copilot' once<br/>to authenticate"]
    B --> Note2["Your app handles login<br/>and passes token"]
    C --> Note3["No GitHub auth needed<br/>Uses your model provider"]

    style App fill:#0d1117,stroke:#58a6ff,color:#c9d1d9

Option A: User's Signed-In Credentials (Simplest)

The user signs in to the CLI once, and your bundled app uses those credentials. No extra code needed — this is the default behavior.

const client = new CopilotClient({
    cliPath: path.join(__dirname, "vendor", "copilot"),
    // Default: uses signed-in user credentials
});

Option B: Token via Environment Variable

Ship your app with instructions to set a token, or set it programmatically:

const client = new CopilotClient({
    cliPath: path.join(__dirname, "vendor", "copilot"),
    env: {
        COPILOT_GITHUB_TOKEN: getUserToken(),  // Your app provides the token
    },
});

Option C: BYOK (No GitHub Auth Needed)

If you manage your own model provider keys, users don't need GitHub accounts at all:

const client = new CopilotClient({
    cliPath: path.join(__dirname, "vendor", "copilot"),
});

const session = await client.createSession({
    model: "gpt-4.1",
    provider: {
        type: "openai",
        baseUrl: "https://api.openai.com/v1",
        apiKey: process.env.OPENAI_API_KEY,
    },
});

See the BYOK guide for full details.

Session Management

Bundled apps typically want named sessions so users can resume conversations:

const client = new CopilotClient({
    cliPath: path.join(__dirname, "vendor", "copilot"),
});

// Create a session tied to the user's project
const sessionId = `project-${projectName}`;
const session = await client.createSession({
    sessionId,
    model: "gpt-4.1",
});

// User closes app...
// Later, resume where they left off
const resumed = await client.resumeSession(sessionId);

Session state persists at ~/.copilot/session-state/{sessionId}/.

Distribution Patterns

Desktop App (Electron, Tauri)

flowchart TB
    subgraph Electron["Desktop App Package"]
        UI["App UI"] --> Main["Main Process"]
        Main --> SDK["SDK Client"]
        SDK --> CLI["Copilot CLI<br/>(in app resources)"]
    end
    CLI --> Cloud["☁️ GitHub Copilot"]

    style Electron fill:#0d1117,stroke:#58a6ff,color:#c9d1d9

Include the CLI binary in your app's resources directory:

import { app } from "electron";
import path from "path";

const cliPath = path.join(
    app.isPackaged ? process.resourcesPath : __dirname,
    "copilot"
);

const client = new CopilotClient({ cliPath });

CLI Tool

For distributable CLI tools, resolve the path relative to your binary:

import { fileURLToPath } from "url";
import path from "path";

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const cliPath = path.join(__dirname, "..", "vendor", "copilot");

const client = new CopilotClient({ cliPath });

Platform-Specific Binaries

When distributing for multiple platforms, include the correct binary for each:

my-app/
├── vendor/
│   ├── copilot-darwin-arm64    # macOS Apple Silicon
│   ├── copilot-darwin-x64      # macOS Intel
│   ├── copilot-linux-x64       # Linux x64
│   └── copilot-win-x64.exe     # Windows x64
└── src/
    └── index.ts
import os from "os";

function getCLIPath(): string {
    const platform = process.platform;   // "darwin", "linux", "win32"
    const arch = os.arch();              // "arm64", "x64"
    const ext = platform === "win32" ? ".exe" : "";
    const name = `copilot-${platform}-${arch}${ext}`;
    return path.join(__dirname, "vendor", name);
}

const client = new CopilotClient({
    cliPath: getCLIPath(),
});

Limitations

LimitationDetails
Bundle sizeCLI binary adds to your app's distribution size
UpdatesYou manage CLI version updates in your release cycle
Platform buildsNeed separate binaries for each OS/architecture
Single userEach bundled CLI instance serves one user

When to Move On

NeedNext Guide
Users signing in with GitHub accountsGitHub OAuth
Run on a server instead of user machinesBackend Services
Use your own model keysBYOK

Next Steps

Can you improve this documentation? These fine people already did:
Jeremiah Isaacson & Patrick Nikoletich
Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close