mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-16 17:55:26 -04:00
feat(provider): add GitHub Copilot provider with device-flow auth (#1480)
* feat(provider): add GitHub Copilot provider with device-flow auth
Adds GitHub Copilot as a model provider, so Copilot models (gpt-4o/4.1/5,
Claude, Gemini, …) work through the normal chat + agent loop, incl. native
tool calling and vision.
Auth is one-click via the GitHub OAuth device flow; the access token is stored
as the endpoint's (encrypted) api_key and sent directly as `Authorization:
Bearer` (no Copilot-token exchange, no refresh — matching how editors talk to
the Copilot API). Copilot is a normal ModelEndpoint detected by host; the only
provider-specific behaviour is a small set of required request headers,
injected centrally.
Sign-in is available from Settings → model endpoints ("Connect GitHub
Copilot") and from chat via `/setup copilot`.
- src/copilot.py (new), routes/copilot_routes.py (new): constants, header
builders, device-flow start/poll, model discovery, owner-scoped endpoint
provisioning.
- src/llm_core.py, src/endpoint_resolver.py: detect `copilot`, inject headers,
per-request x-initiator/vision.
- src/agent_loop.py: allowlist api.githubcopilot.com for native tool schemas.
- src/model_context.py: known context windows for Copilot (no unauthenticated
/models probe).
- static/, README, tests/test_copilot*.py.
* Tidy copilot_routes: clarify supports_tools, note _PENDING is per-process
This commit is contained in:
committed by
GitHub
parent
ca32b43b38
commit
1cd0aa2b8c
@@ -4735,11 +4735,47 @@ function _clearSetupCommandInput() {
|
||||
}
|
||||
}
|
||||
|
||||
// GitHub Copilot device-flow sign-in, driven from chat (mirrors the Settings
|
||||
// "Connect GitHub Copilot" button). Replies via the setup guide messages.
|
||||
async function _setupCopilot() {
|
||||
_clearSetupGuideMessages();
|
||||
await _setupReply('Starting GitHub Copilot sign-in…');
|
||||
let start;
|
||||
try {
|
||||
const r = await fetch(`${API_BASE}/api/copilot/device/start`, { method: 'POST', body: new FormData(), credentials: 'same-origin' });
|
||||
start = await r.json();
|
||||
if (!r.ok) { await _setupReply(start.detail || 'Failed to start Copilot sign-in.'); return; }
|
||||
} catch (e) { await _setupReply('Request failed.'); return; }
|
||||
const authUrl = start.verification_uri_complete || start.verification_uri || '';
|
||||
await _setupReply(`Opening GitHub — approve the request (code ${start.user_code}). Waiting…`);
|
||||
try { if (authUrl) window.open(authUrl, '_blank', 'noopener'); } catch (e) {}
|
||||
const deadline = Date.now() + (start.expires_in || 900) * 1000;
|
||||
const stepMs = Math.max((start.interval || 5), 2) * 1000;
|
||||
const poll = async () => {
|
||||
if (Date.now() > deadline) { await _setupReply('Copilot sign-in expired — run /setup copilot again.'); return; }
|
||||
try {
|
||||
const fd = new FormData(); fd.append('poll_id', start.poll_id);
|
||||
const r = await fetch(`${API_BASE}/api/copilot/device/poll`, { method: 'POST', body: fd, credentials: 'same-origin' });
|
||||
const d = await r.json();
|
||||
if (d.status === 'authorized') {
|
||||
const n = ((d.endpoint && d.endpoint.models) || []).length;
|
||||
await _setupReply(`Connected — ${n} Copilot model${n !== 1 ? 's' : ''} available.`);
|
||||
if (modelsModule) modelsModule.refreshModels(true);
|
||||
return;
|
||||
}
|
||||
if (d.status === 'failed') { await _setupReply('Copilot sign-in failed (' + (d.error || 'denied') + ').'); return; }
|
||||
} catch (e) { /* transient — keep polling */ }
|
||||
setTimeout(poll, stepMs);
|
||||
};
|
||||
setTimeout(poll, stepMs);
|
||||
}
|
||||
|
||||
async function _cmdSetup(args, ctx) {
|
||||
_hideWelcomeScreen();
|
||||
_clearSetupCommandInput();
|
||||
const topic = (args[0] || '').trim().toLowerCase();
|
||||
const topicArgs = args.slice(1);
|
||||
if (topic === 'copilot' || topic === 'github') { await _setupCopilot(); return true; }
|
||||
const provider = _setupProviderFromInput(topic);
|
||||
if (provider) {
|
||||
_clearSetupGuideMessages();
|
||||
@@ -5464,7 +5500,7 @@ const COMMANDS = {
|
||||
category: 'Getting started',
|
||||
help: 'Add local or API model endpoints',
|
||||
handler: _cmdSetup,
|
||||
usage: '/setup local URL · /setup groq KEY · /setup endpoint'
|
||||
usage: '/setup local URL · /setup groq KEY · /setup copilot · /setup endpoint'
|
||||
},
|
||||
demo: {
|
||||
alias: ['tour'],
|
||||
|
||||
Reference in New Issue
Block a user