diff --git a/deploy/workspace/Dockerfile b/deploy/workspace/Dockerfile index 33757dfab..9885c6d23 100644 --- a/deploy/workspace/Dockerfile +++ b/deploy/workspace/Dockerfile @@ -125,6 +125,12 @@ RUN mkdir -p /home/workspace/.codex && \ > /home/workspace/.codex/config.toml && \ chown -R workspace:workspace /home/workspace/.codex +# Configure GitHub Copilot MCP settings +RUN mkdir -p /home/workspace/.config/github-copilot && \ + echo '{"mcpServers":{"agent-relay":{"command":"node","args":["/app/dist/src/cli/index.js","mcp","serve"]}}}' \ + > /home/workspace/.config/github-copilot/mcp.json && \ + chown -R workspace:workspace /home/workspace/.config/github-copilot + # Run entrypoint as root so it can start sshd; entrypoint drops to workspace via gosu USER root diff --git a/deploy/workspace/Dockerfile.base b/deploy/workspace/Dockerfile.base index 9fbd1474a..d19d3759e 100644 --- a/deploy/workspace/Dockerfile.base +++ b/deploy/workspace/Dockerfile.base @@ -64,7 +64,8 @@ RUN npm install -g \ @anthropic-ai/claude-code@2.1.17 \ @openai/codex@0.88.0 \ @google/gemini-cli@0.25.1 \ - opencode-ai@1.1.34 + opencode-ai@1.1.34 \ + @github/copilot@0.0.395 # Create workspace directory structure RUN mkdir -p /workspace /data diff --git a/deploy/workspace/Dockerfile.browser b/deploy/workspace/Dockerfile.browser index f2f9d53d3..eb24028ec 100644 --- a/deploy/workspace/Dockerfile.browser +++ b/deploy/workspace/Dockerfile.browser @@ -124,6 +124,7 @@ RUN npm install -g @anthropic-ai/claude-code@2.0.67 RUN npm install -g @openai/codex@0.78.0 RUN npm install -g @google/gemini-cli@0.22.5 RUN npm install -g opencode-ai@1.1.3 +RUN npm install -g @github/copilot@0.0.395 # Install Playwright with browsers RUN npm install -g playwright RUN npx playwright install chromium firefox diff --git a/deploy/workspace/Dockerfile.local b/deploy/workspace/Dockerfile.local index 80f183987..af751ca20 100644 --- a/deploy/workspace/Dockerfile.local +++ b/deploy/workspace/Dockerfile.local @@ -111,6 +111,7 @@ RUN npm install -g @anthropic-ai/claude-code@2.0.67 RUN npm install -g @openai/codex@0.78.0 RUN npm install -g @google/gemini-cli@0.22.5 RUN npm install -g opencode-ai@1.1.3 +RUN npm install -g @github/copilot@0.0.395 # Create workspace directory structure RUN mkdir -p /workspace /data diff --git a/packages/config/src/cli-auth-config.ts b/packages/config/src/cli-auth-config.ts index fb6ffa2c1..52af67d13 100644 --- a/packages/config/src/cli-auth-config.ts +++ b/packages/config/src/cli-auth-config.ts @@ -329,6 +329,68 @@ export const CLI_AUTH_CONFIG: Record = { }, ], }, + copilot: { + command: 'copilot', + args: ['auth', 'login'], // copilot auth login - triggers GitHub OAuth flow + deviceFlowArgs: ['auth', 'login', '--device'], // Device flow for headless environments + supportsDeviceFlow: true, + urlPattern: /(https:\/\/[^\s]+)/, + // Copilot uses gh CLI's auth - credentials stored via GitHub CLI config + credentialPath: '~/.config/gh/hosts.yml', + displayName: 'GitHub Copilot', + waitTimeout: 30000, + prompts: [ + { + // Browser or device code selection + pattern: /login\s*with\s*a\s*code|one-time\s*code|device\s*code|browser|authenticate/i, + response: '\r', // Select first option (browser-based auth) + delay: 200, + description: 'Auth method selection', + }, + { + // Press Enter to open browser + pattern: /press\s*enter\s*to\s*open|open.*browser|opening\s*browser/i, + response: '\r', + delay: 200, + description: 'Open browser prompt', + }, + { + // Login success - press enter to continue + pattern: /login\s*successful|logged\s*in.*press\s*enter|press\s*enter\s*to\s*continue|authentication\s*complete/i, + response: '\r', + delay: 200, + description: 'Login success prompt', + }, + { + // Generic enter prompt (fallback) + pattern: /press\s*enter|enter\s*to\s*(confirm|continue|proceed)/i, + response: '\r', + delay: 300, + description: 'Generic enter prompt', + }, + ], + successPatterns: [/success/i, /authenticated/i, /logged\s*in/i, /you.*(?:are|now).*logged/i, /authentication\s*complete/i], + errorPatterns: [ + { + pattern: /oauth\s*error|auth.*failed|authentication\s*error/i, + message: 'GitHub authentication failed', + recoverable: true, + hint: 'Please try logging in again. Make sure you have GitHub Copilot access.', + }, + { + pattern: /network\s*error|ENOTFOUND|ECONNREFUSED|timeout/i, + message: 'Network error during authentication', + recoverable: true, + hint: 'Please check your internet connection and try again.', + }, + { + pattern: /no\s*copilot\s*access|copilot\s*not\s*enabled|subscription\s*required/i, + message: 'GitHub Copilot access not available', + recoverable: false, + hint: 'Your GitHub account needs an active Copilot subscription or organization access.', + }, + ], + }, }; /** diff --git a/packages/wrapper/src/shared.ts b/packages/wrapper/src/shared.ts index c1dcc001f..a1449b4ae 100644 --- a/packages/wrapper/src/shared.ts +++ b/packages/wrapper/src/shared.ts @@ -85,7 +85,7 @@ export interface InjectionMetrics { /** * CLI types for special handling */ -export type CliType = 'claude' | 'codex' | 'gemini' | 'droid' | 'opencode' | 'cursor' | 'spawned' | 'other'; +export type CliType = 'claude' | 'codex' | 'gemini' | 'droid' | 'opencode' | 'cursor' | 'copilot' | 'spawned' | 'other'; /** * Injection timing constants @@ -293,6 +293,7 @@ export function detectCliType(command: string): CliType { if (cmdLower.includes('droid')) return 'droid'; if (cmdLower.includes('opencode')) return 'opencode'; if (cmdLower.includes('cursor')) return 'cursor'; + if (cmdLower.includes('copilot')) return 'copilot'; return 'other'; } @@ -312,7 +313,7 @@ export const CLI_QUIRKS = { * Others may interpret the escape sequences literally. */ supportsBracketedPaste: (cli: CliType): boolean => { - return cli === 'claude' || cli === 'codex' || cli === 'gemini' || cli === 'opencode' || cli === 'cursor'; + return cli === 'claude' || cli === 'codex' || cli === 'gemini' || cli === 'opencode' || cli === 'cursor' || cli === 'copilot'; }, /** @@ -335,6 +336,7 @@ export const CLI_QUIRKS = { droid: /^[>›»]\s*$/, opencode: /^[>›»]\s*$/, cursor: /^[>›»]\s*$/, + copilot: /^[>›»]\s*$/, spawned: /^[>›»]\s*$/, other: /^[>$%#➜›»]\s*$/, };