Created
February 20, 2026 23:12
-
-
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`
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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