Skip to content

Instantly share code, notes, and snippets.

@ken-morel
Created February 20, 2026 23:12
Show Gist options
  • Select an option

  • Save ken-morel/577a8b74606bd034150e32aa758ce60f to your computer and use it in GitHub Desktop.

Select an option

Save ken-morel/577a8b74606bd034150e32aa758ce60f to your computer and use it in GitHub Desktop.
A gemini-generated js file to authenticate copilot-lsp for you, run with `bun file.js /path/to/copilot-lsp`
const { spawn } = require('child_process');
// 1. Setup Process
const serverPath = process.argv[2];
if (!serverPath) {
console.error("❌ Please provide the path to the Copilot Language Server.");
console.error("Usage: node auth.js <path-to-copilot-language-server.js>");
console.error(" or: bun run auth.js <path-to-copilot-language-server.js>");
process.exit(1);
}
// Automatically use the current runtime (node or bun) if it's a JS file
const isJs = serverPath.endsWith('.js');
const command = isJs ? process.execPath : serverPath;
const args = isJs ? [serverPath, '--stdio'] : ['--stdio'];
console.log(`πŸš€ Starting Copilot Language Server via: ${command} ${args.join(' ')}`);
const ls = spawn(command, args, { stdio: ['pipe', 'pipe', 'inherit'] });
// 2. JSON-RPC Communication Helpers
let msgId = 1;
const pendingRequests = new Map();
function sendRequest(method, params) {
return new Promise((resolve, reject) => {
const id = msgId++;
const msg = { jsonrpc: "2.0", id, method, params };
const json = JSON.stringify(msg);
const payload = `Content-Length: ${Buffer.byteLength(json)}\r\n\r\n${json}`;
pendingRequests.set(id, { resolve, reject });
ls.stdin.write(payload);
});
}
function sendNotification(method, params) {
const msg = { jsonrpc: "2.0", method, params };
const json = JSON.stringify(msg);
const payload = `Content-Length: ${Buffer.byteLength(json)}\r\n\r\n${json}`;
ls.stdin.write(payload);
}
function sendResponse(id, result) {
const msg = { jsonrpc: "2.0", id, result };
const json = JSON.stringify(msg);
const payload = `Content-Length: ${Buffer.byteLength(json)}\r\n\r\n${json}`;
ls.stdin.write(payload);
}
// 3. Stream Parser (Handles LSP Data Chunking)
let buffer = Buffer.alloc(0);
ls.stdout.on('data', (chunk) => {
buffer = Buffer.concat([buffer, chunk]);
while (true) {
// Find the HTTP-style header length
const match = buffer.toString('ascii').match(/Content-Length: (\d+)\r\n\r\n/);
if (!match) break;
const headerLength = match[0].length;
const contentLength = parseInt(match[1], 10);
// Break if we haven't received the full message body yet
if (buffer.length < headerLength + contentLength) break;
// Extract the JSON body and advance the buffer
const body = buffer.subarray(headerLength, headerLength + contentLength).toString('utf8');
buffer = buffer.subarray(headerLength + contentLength);
const msg = JSON.parse(body);
handleMessage(msg);
}
});
// 4. Message Routing & Logic
function handleMessage(msg) {
// A) Route responses back to our waiting Promises
if (msg.id !== undefined && pendingRequests.has(msg.id)) {
const { resolve, reject } = pendingRequests.get(msg.id);
if (msg.error) reject(msg.error);
else resolve(msg.result);
pendingRequests.delete(msg.id);
}
// B) Handle Server Status Changes
else if (msg.method === 'didChangeStatus') {
const { kind, message } = msg.params || {};
console.log(`\n[Status Change] ${kind}${message ? `: ${message}` : ''}`);
if (kind === 'Normal') {
console.log("\nβœ… Successfully authenticated with GitHub Copilot!");
console.log("The token is now stored. Your LSP client can connect freely.");
process.exit(0);
}
}
// C) Handle URL opening requests from the server
else if (msg.method === 'window/showDocument') {
console.log(`\nπŸ”— Server requested to open: ${msg.params.uri}`);
if (msg.id !== undefined) {
sendResponse(msg.id, { success: true });
}
}
}
// 5. Main Authentication Protocol Flow
async function run() {
try {
console.log("βš™οΈ Sending 'initialize' request...");
await sendRequest('initialize', {
processId: process.pid,
capabilities: { workspace: { workspaceFolders: true } },
initializationOptions: {
editorInfo: { name: "CopilotAuthScript", version: "1.0.0" },
editorPluginInfo: { name: "CopilotAuth", version: "1.0.0" }
}
});
console.log("βš™οΈ Sending 'initialized' notification...");
sendNotification('initialized', {});
console.log("πŸ” Requesting sign-in...");
const signInResult = await sendRequest('signIn', {});
if (signInResult && signInResult.userCode) {
console.log("\n===========================================================");
console.log("πŸ”‘ ACTION REQUIRED");
console.log(`1. Copy your authorization code: \x1b[32m${signInResult.userCode}\x1b[0m`);
console.log(`2. A browser will open, or visit: https://github.com/login/device`);
console.log(`3. Paste the code to authorize the Copilot LSP.`);
console.log("===========================================================\n");
console.log("⏳ Waiting for you to complete sign-in in your browser...");
// Execute the finishDeviceFlow command to initiate server polling
await sendRequest('workspace/executeCommand', {
command: signInResult.command.command,
arguments: signInResult.command.arguments
});
} else {
console.log("\nℹ️ Already authenticated or unexpected response:");
console.log(signInResult);
}
} catch (err) {
console.error("❌ Error during communication:", err);
process.exit(1);
}
}
// Start the sequence
run();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment