Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions nodejs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -133,5 +133,8 @@ dist
dist/
build/

# Generated version constants (regenerated at build time)
src/generated/

# macOS
.DS_Store
63 changes: 60 additions & 3 deletions nodejs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,10 @@ new CopilotClient(options?: CopilotClientOptions)

**Options:**

- `cliPath?: string` - Path to CLI executable (default: "copilot" from PATH)
- `cliPath?: string` - Path to CLI executable (default: "copilot" from PATH). Mutually exclusive with `acquisition`.
- `cliArgs?: string[]` - Extra arguments prepended before SDK-managed flags (e.g. `["./dist-cli/index.js"]` when using `node`)
- `cliUrl?: string` - URL of existing CLI server to connect to (e.g., `"localhost:8080"`, `"http://127.0.0.1:9000"`, or just `"8080"`). When provided, the client will not spawn a CLI process.
- `cliUrl?: string` - URL of existing CLI server to connect to (e.g., `"localhost:8080"`, `"http://127.0.0.1:9000"`, or just `"8080"`). When provided, the client will not spawn a CLI process. Mutually exclusive with `acquisition`.
- `acquisition?: AcquisitionOptions` - Auto-download CLI if not present. See [CLI Acquisition](#cli-acquisition). Mutually exclusive with `cliPath` and `cliUrl`.
- `port?: number` - Server port (default: 0 for random)
- `useStdio?: boolean` - Use stdio transport instead of TCP (default: true)
- `logLevel?: string` - Log level (default: "info")
Expand Down Expand Up @@ -645,7 +646,63 @@ try {
## Requirements

- Node.js >= 18.0.0
- GitHub Copilot CLI installed and in PATH (or provide custom `cliPath`)
- GitHub Copilot CLI installed and in PATH, or:
- Provide a custom `cliPath`, or
- Use `acquisition` to auto-download the CLI (see below)

## CLI Acquisition

The SDK can automatically download and manage the Copilot CLI for you. This is useful when you can't rely on the CLI being pre-installed.

```typescript
const client = new CopilotClient({
acquisition: {
downloadDir: "~/.myapp/copilot-cli", // Where to store CLI versions
},
});

await client.start(); // Downloads CLI if needed, then starts
```

### How it works

1. **Check existing downloads**: Looks for any CLI version in `downloadDir` that is both >= your `minVersion` (if specified) and protocol-compatible with this SDK.

2. **If a suitable version exists**: Uses the highest compatible version. No download needed.

3. **If no suitable version exists**: Downloads the CLI version this SDK was built for.

This means SDK upgrades don't force re-downloads — your existing CLI is reused as long as it still works.

### Options

```typescript
interface AcquisitionOptions {
// Required: Directory for CLI downloads. Should be app-specific.
downloadDir: string;

// Optional: Minimum CLI version required (e.g., "0.0.405").
// If your existing version is lower, a new version will be downloaded.
minVersion?: string;

// Optional: Progress callback for download updates.
onProgress?: (progress: { bytesDownloaded: number; totalBytes: number }) => void;
}
```

### Example with progress reporting

```typescript
const client = new CopilotClient({
acquisition: {
downloadDir: path.join(os.homedir(), ".myapp", "copilot-cli"),
onProgress: ({ bytesDownloaded, totalBytes }) => {
const pct = totalBytes > 0 ? Math.round((bytesDownloaded / totalBytes) * 100) : 0;
console.log(`Downloading CLI: ${pct}%`);
},
},
});
```

## License

Expand Down
21 changes: 18 additions & 3 deletions nodejs/examples/basic-example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------------------------------------------*/

import { homedir } from "node:os";
import { join } from "node:path";
import { z } from "zod";
import { CopilotClient, defineTool } from "../src/index.js";

Expand All @@ -20,10 +22,23 @@ const lookupFactTool = defineTool("lookup_fact", {
handler: ({ topic }) => facts[topic.toLowerCase()] ?? `No fact stored for ${topic}.`,
});

// Create client - will auto-start CLI server (searches PATH for "copilot")
const client = new CopilotClient({ logLevel: "info" });
// Create client with automatic CLI acquisition
const client = new CopilotClient({
logLevel: "info",
acquisition: {
downloadDir: join(homedir(), ".copilot-sdk-example", "cli"),
minVersion: "0.0.400",
onProgress: ({ bytesDownloaded, totalBytes }) => {
if (totalBytes > 0) {
const pct = Math.round((bytesDownloaded / totalBytes) * 100);
process.stdout.write(`\r⬇️ Downloading CLI: ${pct}%`);
}
},
},
});

const session = await client.createSession({ tools: [lookupFactTool] });
console.log(`✅ Session created: ${session.sessionId}\n`);
console.log(`\n✅ Session created: ${session.sessionId}\n`);

// Listen to events
session.on((event) => {
Expand Down
11 changes: 9 additions & 2 deletions nodejs/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions nodejs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
},
"type": "module",
"scripts": {
"clean": "rimraf --glob dist *.tgz",
"clean": "rimraf --glob dist *.tgz src/generated",
"prebuild": "tsx scripts/generate-versions.ts",
"build": "tsx esbuild-copilotsdk-nodejs.ts",
"test": "vitest run",
"test:watch": "vitest",
Expand All @@ -41,11 +42,13 @@
"license": "MIT",
"dependencies": {
"@github/copilot": "^0.0.402",
"semver": "^7.7.3",
"vscode-jsonrpc": "^8.2.1",
"zod": "^4.3.5"
},
"devDependencies": {
"@types/node": "^25.2.0",
"@types/semver": "^7.7.1",
"@typescript-eslint/eslint-plugin": "^8.54.0",
"@typescript-eslint/parser": "^8.54.0",
"esbuild": "^0.27.2",
Expand All @@ -56,7 +59,6 @@
"prettier": "^3.4.0",
"quicktype-core": "^23.2.6",
"rimraf": "^6.1.2",
"semver": "^7.7.3",
"tsx": "^4.20.6",
"typescript": "^5.0.0",
"vitest": "^4.0.18"
Expand Down
18 changes: 18 additions & 0 deletions nodejs/scripts/generate-versions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Generates src/generated/versions.ts from package-lock.json @github/copilot version.
// Run automatically via prebuild hook.

import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const packageLock = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "package-lock.json"), "utf-8"));
const cliVersion = packageLock.packages["node_modules/@github/copilot"].version;

const code = `// Generated by scripts/generate-versions.ts - DO NOT EDIT
export const PREFERRED_CLI_VERSION = "${cliVersion}";
`;

fs.mkdirSync(path.join(__dirname, "..", "src", "generated"), { recursive: true });
fs.writeFileSync(path.join(__dirname, "..", "src", "generated", "versions.ts"), code);
console.log(`Generated src/generated/versions.ts with PREFERRED_CLI_VERSION="${cliVersion}"`);
Loading
Loading