* feat(ai): integrate GitHub Copilot support and enhance agent management - Added GitHub Copilot as a new AI provider, including connection and installation handling. - Updated agent settings to include Copilot in the configuration options. - Enhanced the chat API to support Copilot as a provider, allowing for model selection and streaming responses. - Implemented installation instructions for Copilot CLI within the application. - Updated UI components to display Copilot status and connection options. - Enhanced internationalization support with new translations for Copilot-related terms. Bump version to 0.2.1. * feat(electron): enhance theme management for Windows/Linux - Added support for dynamic title bar overlay based on theme selection for Windows and Linux platforms. - Implemented auto-hide menu bar functionality for improved user experience on Windows/Linux. - Updated Electron API to include a new `setTheme` method for theme synchronization. - Modified top bar component to trigger theme changes via the new API, ensuring consistent UI across platforms. * feat(electron): add Linux window controls detection and styling - Implemented a function to detect the position of window controls on Linux, supporting both left and right placements based on desktop environment settings. - Updated the main window styling to adjust control padding dynamically for Linux and Windows platforms, ensuring a consistent user interface across operating systems. - Modified the top bar component to include specific class names for better control layout management. * fix(electron): update title bar overlay color and height for consistency - Changed title bar overlay color to transparent (rgba(0,0,0,0)) for improved aesthetics. - Adjusted height of the title bar from 40 to 36 pixels to enhance visual alignment across platforms. * fix(editor): adjust position of update ready banner for improved visibility - Moved the update ready banner from top-5 to top-12 to enhance its visibility and prevent overlap with other UI elements.
187 lines
5.2 KiB
TypeScript
187 lines
5.2 KiB
TypeScript
import { defineEventHandler, readBody, setResponseHeaders } from 'h3'
|
|
import { execSync } from 'node:child_process'
|
|
|
|
interface InstallBody {
|
|
agent: 'claude-code' | 'codex-cli' | 'opencode' | 'copilot'
|
|
}
|
|
|
|
interface InstallResult {
|
|
success: boolean
|
|
error?: string
|
|
command?: string
|
|
docsUrl?: string
|
|
}
|
|
|
|
const BINARY_MAP: Record<string, string> = {
|
|
'claude-code': 'claude',
|
|
'codex-cli': 'codex',
|
|
'opencode': 'opencode',
|
|
'copilot': 'copilot',
|
|
}
|
|
|
|
function checkBinary(binary: string): boolean {
|
|
try {
|
|
const cmd = process.platform === 'win32'
|
|
? `where ${binary} 2>nul`
|
|
: `which ${binary} 2>/dev/null`
|
|
return !!execSync(cmd, { encoding: 'utf-8', timeout: 5000 }).trim()
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
|
|
function hasCommand(cmd: string): boolean {
|
|
return checkBinary(cmd)
|
|
}
|
|
|
|
function getInstallInfo(agent: string): { command: string; docsUrl: string } {
|
|
const isWin = process.platform === 'win32'
|
|
const isMac = process.platform === 'darwin'
|
|
|
|
switch (agent) {
|
|
case 'claude-code':
|
|
return {
|
|
command: 'npm install -g @anthropic-ai/claude-code',
|
|
docsUrl: 'https://docs.anthropic.com/en/docs/claude-code',
|
|
}
|
|
case 'codex-cli':
|
|
return {
|
|
command: 'npm install -g @openai/codex',
|
|
docsUrl: 'https://github.com/openai/codex',
|
|
}
|
|
case 'opencode':
|
|
return {
|
|
command: isWin
|
|
? 'go install github.com/opencode-ai/opencode@latest'
|
|
: 'curl -fsSL https://opencode.ai/install | bash',
|
|
docsUrl: 'https://opencode.ai',
|
|
}
|
|
case 'copilot':
|
|
return {
|
|
command: isMac
|
|
? 'brew install github/copilot/copilot'
|
|
: isWin
|
|
? 'winget install GitHub.CopilotCLI'
|
|
: 'See documentation',
|
|
docsUrl: 'https://docs.github.com/copilot/how-tos/copilot-cli',
|
|
}
|
|
default:
|
|
return { command: '', docsUrl: '' }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* POST /api/ai/install-agent
|
|
* Attempts to auto-install a CLI agent. Returns manual instructions on failure.
|
|
*/
|
|
export default defineEventHandler(async (event) => {
|
|
const body = await readBody<InstallBody>(event)
|
|
setResponseHeaders(event, { 'Content-Type': 'application/json' })
|
|
|
|
if (!body?.agent) {
|
|
return { success: false, error: 'Missing agent field' } satisfies InstallResult
|
|
}
|
|
|
|
const binary = BINARY_MAP[body.agent]
|
|
if (!binary) {
|
|
return { success: false, error: `Unknown agent: ${body.agent}` } satisfies InstallResult
|
|
}
|
|
|
|
// Already installed
|
|
if (checkBinary(binary)) {
|
|
return { success: true } satisfies InstallResult
|
|
}
|
|
|
|
const info = getInstallInfo(body.agent)
|
|
|
|
// Try auto-install
|
|
const result = await tryAutoInstall(body.agent, binary)
|
|
if (result.success) return result
|
|
|
|
// Return failure with manual instructions
|
|
return {
|
|
success: false,
|
|
error: result.error || 'Auto-install failed',
|
|
command: info.command,
|
|
docsUrl: info.docsUrl,
|
|
} satisfies InstallResult
|
|
})
|
|
|
|
async function tryAutoInstall(agent: string, binary: string): Promise<InstallResult> {
|
|
switch (agent) {
|
|
case 'claude-code':
|
|
return tryNpmInstall('@anthropic-ai/claude-code', binary)
|
|
case 'codex-cli':
|
|
return tryNpmInstall('@openai/codex', binary)
|
|
case 'opencode':
|
|
return tryOpenCodeInstall(binary)
|
|
case 'copilot':
|
|
return tryCopilotInstall(binary)
|
|
default:
|
|
return { success: false, error: 'Unknown agent' }
|
|
}
|
|
}
|
|
|
|
async function tryNpmInstall(pkg: string, binary: string): Promise<InstallResult> {
|
|
if (!hasCommand('npm')) {
|
|
return { success: false, error: 'npm not found. Install Node.js first.' }
|
|
}
|
|
|
|
try {
|
|
execSync(`npm install -g ${pkg}`, {
|
|
encoding: 'utf-8',
|
|
timeout: 180_000,
|
|
stdio: 'pipe',
|
|
})
|
|
} catch (err) {
|
|
const stderr = (err as { stderr?: string }).stderr || ''
|
|
const msg = stderr.includes('EACCES')
|
|
? 'Permission denied. Try running with sudo or fix npm permissions.'
|
|
: err instanceof Error ? err.message : 'npm install failed'
|
|
return { success: false, error: msg }
|
|
}
|
|
|
|
return checkBinary(binary)
|
|
? { success: true }
|
|
: { success: false, error: 'Install completed but binary not found in PATH' }
|
|
}
|
|
|
|
async function tryOpenCodeInstall(binary: string): Promise<InstallResult> {
|
|
if (process.platform === 'win32') {
|
|
return { success: false, error: 'Auto-install not supported on Windows' }
|
|
}
|
|
|
|
try {
|
|
execSync('curl -fsSL https://opencode.ai/install | bash', {
|
|
encoding: 'utf-8',
|
|
timeout: 120_000,
|
|
stdio: 'pipe',
|
|
})
|
|
} catch (err) {
|
|
const msg = err instanceof Error ? err.message : 'Install failed'
|
|
return { success: false, error: msg }
|
|
}
|
|
|
|
return checkBinary(binary)
|
|
? { success: true }
|
|
: { success: false, error: 'Install completed but binary not found in PATH' }
|
|
}
|
|
|
|
async function tryCopilotInstall(binary: string): Promise<InstallResult> {
|
|
// Try brew on macOS
|
|
if (process.platform === 'darwin' && hasCommand('brew')) {
|
|
try {
|
|
execSync('brew install github/copilot/copilot', {
|
|
encoding: 'utf-8',
|
|
timeout: 180_000,
|
|
stdio: 'pipe',
|
|
})
|
|
if (checkBinary(binary)) return { success: true }
|
|
} catch {
|
|
// Fall through to failure
|
|
}
|
|
}
|
|
|
|
return { success: false, error: 'Auto-install not available for this platform' }
|
|
}
|