100 lines
3.1 KiB
Python
100 lines
3.1 KiB
Python
import json
|
|
import uuid
|
|
import requests
|
|
import time
|
|
import secrets
|
|
|
|
from pathlib import Path
|
|
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
|
|
|
|
def get_copilot_token_from_neovim_vault():
|
|
"""Read Copilot token from Neovim's vault_state.json."""
|
|
vault_path = Path.home() / ".local/share/lazyvim/gp/persisted/vault_state.json"
|
|
|
|
try:
|
|
with open(vault_path, 'r') as f:
|
|
vault = json.load(f)
|
|
return vault['copilot_bearer']['token']
|
|
except (FileNotFoundError, KeyError, json.JSONDecodeError) as e:
|
|
raise Exception(f"Failed to read Copilot token from vault: {e}")
|
|
|
|
|
|
def gen_hex_str(n):
|
|
return secrets.token_hex(n)
|
|
|
|
|
|
class CopilotOpenAIProxy(BaseHTTPRequestHandler):
|
|
DEFAULT_HEADERS = {
|
|
'Editor-Version': "vscode/1.83.1",
|
|
'Editor-Plugin-Version': "copilot-chat/0.8.0",
|
|
'Openai-Organization': "github-copilot",
|
|
'Openai-Intent': "conversation-panel",
|
|
'Content-Type': "text/event-stream; charset=utf-8",
|
|
'User-Agent': "GitHubCopilotChat/0.8.0",
|
|
'Accept': "*/*",
|
|
'Accept-Encoding': "gzip,deflate,br",
|
|
}
|
|
copilot_token = None
|
|
|
|
def __init__(self, token):
|
|
self.copilot_token = token
|
|
|
|
def __call__(self, *args, **kwargs):
|
|
"""Handle a request."""
|
|
super().__init__(*args, **kwargs)
|
|
|
|
def request_id(self):
|
|
return gen_hex_str(4) + "-" + gen_hex_str(2) + "-" + gen_hex_str(2) + "-" + gen_hex_str(2) + "-" + gen_hex_str(6)
|
|
|
|
def session_id(self):
|
|
return gen_hex_str(4) + "-" + gen_hex_str(2) + "-" + gen_hex_str(2) + "-" + gen_hex_str(2) + "-" + gen_hex_str(12)
|
|
|
|
def machine_id(self):
|
|
return gen_hex_str(32)
|
|
|
|
def do_POST(self):
|
|
if self.path != '/v1/chat/completions':
|
|
self.send_response(
|
|
requests.codes.method_not_allowed, "Method Not Allowed")
|
|
self.end_headers()
|
|
|
|
return
|
|
|
|
content_length = int(self.headers['Content-Length'])
|
|
body = self.rfile.read(content_length)
|
|
copilot_resp = self.copilot_request(body)
|
|
|
|
if copilot_resp.status_code != requests.codes.ok:
|
|
self.send_response(copilot_resp.status_code)
|
|
self.end_headers()
|
|
|
|
return
|
|
|
|
self.send_response(requests.codes.ok)
|
|
self.end_headers()
|
|
self.wfile.write(copilot_resp.content)
|
|
print(copilot_resp.content)
|
|
|
|
def copilot_request(self, body):
|
|
url = "https://api.githubcopilot.com/chat/completions"
|
|
|
|
headers = self.DEFAULT_HEADERS.copy()
|
|
headers['Authorization'] = f'Bearer {self.copilot_token}'
|
|
headers['X-Request-ID'] = self.request_id()
|
|
headers['Vscode-Sessionid'] = self.session_id()
|
|
headers['Vscode-Machineid'] = self.machine_id()
|
|
|
|
return requests.post(url, headers=headers, data=body)
|
|
pass
|
|
|
|
|
|
if __name__ == '__main__':
|
|
token = get_copilot_token_from_neovim_vault()
|
|
proxy = CopilotOpenAIProxy(token)
|
|
|
|
server_address = ('127.0.0.1', 3040)
|
|
httpd = HTTPServer(server_address, proxy)
|
|
print("Proxy running at http://127.0.0.1:3040")
|
|
httpd.serve_forever()
|