automagik-genie
Version:
Self-evolving AI agent orchestration framework with Model Context Protocol support
375 lines (374 loc) • 13.3 kB
JavaScript
;
/**
* Executor authentication helpers
* Auto-configures executors during init and checks auth during run
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkExecutorAuth = checkExecutorAuth;
exports.promptExecutorLogin = promptExecutorLogin;
exports.configureExecutor = configureExecutor;
const child_process_1 = require("child_process");
const fs_1 = require("fs");
const path_1 = __importDefault(require("path"));
const os_1 = __importDefault(require("os"));
const readline_1 = __importDefault(require("readline"));
/**
* Check if an executor is authenticated
*/
async function checkExecutorAuth(executorId) {
switch (executorId) {
case 'OPENCODE':
return checkOpenCodeAuth();
case 'CLAUDE_CODE':
return checkClaudeAuth();
case 'CODEX':
return checkCodexAuth();
case 'GEMINI':
return checkGeminiAuth();
case 'CURSOR':
return checkCursorAuth();
case 'COPILOT':
return checkCopilotAuth();
case 'QWEN_CODE':
return checkQwenAuth();
default:
// Unknown executor, assume it doesn't need auth
return true;
}
}
/**
* Prompt user to configure executor authentication
*/
async function promptExecutorLogin(executorId) {
console.log(`\n⚠️ ${getExecutorName(executorId)} is not configured`);
console.log('Would you like to configure it now? (Y/n)');
const answer = await promptYesNo();
if (!answer) {
console.log('Skipping authentication. You can configure later.');
return;
}
await configureExecutor(executorId);
}
/**
* Configure executor authentication (interactive)
*/
async function configureExecutor(executorId) {
switch (executorId) {
case 'OPENCODE':
await configureOpenCode();
break;
case 'CLAUDE_CODE':
await configureClaude();
break;
case 'CODEX':
await configureCodex();
break;
case 'GEMINI':
await configureGemini();
break;
case 'CURSOR':
await configureCursor();
break;
case 'COPILOT':
await configureCopilot();
break;
case 'QWEN_CODE':
await configureQwen();
break;
}
}
// OpenCode
async function checkOpenCodeAuth() {
try {
// OpenCode stores credentials in ~/.local/share/opencode/auth.json
const authPath = path_1.default.join(os_1.default.homedir(), '.local', 'share', 'opencode', 'auth.json');
const authData = await fs_1.promises.readFile(authPath, 'utf-8');
const parsed = JSON.parse(authData);
// Check if there are any credential entries (object with keys)
return typeof parsed === 'object' && parsed !== null && Object.keys(parsed).length > 0;
}
catch {
return false;
}
}
async function configureOpenCode() {
return new Promise((resolve, reject) => {
console.log(`\nLaunching OpenCode authentication...`);
const child = (0, child_process_1.spawn)('npx', ['@opencode/cli', 'auth', 'login'], {
stdio: 'inherit',
shell: false,
});
child.on('exit', (code) => {
if (code === 0) {
console.log('✓ OpenCode configured');
resolve();
}
else {
reject(new Error(`OpenCode auth failed with code ${code}`));
}
});
child.on('error', (error) => {
reject(new Error(`Failed to launch OpenCode: ${error.message}`));
});
});
}
// Claude Code
async function checkClaudeAuth() {
return new Promise((resolve) => {
const child = (0, child_process_1.spawn)('claude', ['--version'], {
stdio: 'pipe',
shell: false,
});
child.on('exit', (code) => resolve(code === 0));
child.on('error', () => resolve(false));
});
}
async function configureClaude() {
return new Promise((resolve, reject) => {
console.log(`\nLaunching Claude Code token setup...`);
console.log('This requires a Claude subscription.\n');
const child = (0, child_process_1.spawn)('npx', ['@anthropic-ai/claude-code@latest', 'setup-token'], {
stdio: 'inherit',
shell: false,
});
child.on('exit', (code) => {
if (code === 0) {
console.log('\n✓ Claude Code configured');
resolve();
}
else {
reject(new Error(`Claude setup-token failed with code ${code}`));
}
});
child.on('error', (error) => {
reject(new Error(`Failed to launch Claude: ${error.message}`));
});
});
}
// Codex
async function checkCodexAuth() {
try {
const authPath = path_1.default.join(os_1.default.homedir(), '.codex', 'auth.json');
const authData = await fs_1.promises.readFile(authPath, 'utf-8');
const parsed = JSON.parse(authData);
return !!(parsed.OPENAI_API_KEY ||
(parsed.tokens?.access_token && parsed.tokens?.refresh_token));
}
catch {
return false;
}
}
async function configureCodex() {
return new Promise((resolve, reject) => {
console.log(`\nLaunching Codex authentication...\n`);
const child = (0, child_process_1.spawn)('npx', ['@openai/codex@latest', 'login'], {
stdio: 'inherit',
shell: false,
});
child.on('exit', (code) => {
if (code === 0) {
console.log('\n✓ Codex configured');
resolve();
}
else {
reject(new Error(`Codex login failed with code ${code}`));
}
});
child.on('error', (error) => {
reject(new Error(`Failed to launch Codex: ${error.message}`));
});
});
}
// Gemini
async function checkGeminiAuth() {
try {
// Gemini stores config in ~/.gemini/ directory
const geminiDir = path_1.default.join(os_1.default.homedir(), '.gemini');
// Check for OAuth authentication (oauth_creds.json with refresh_token)
const oauthPath = path_1.default.join(geminiDir, 'oauth_creds.json');
try {
const oauthData = await fs_1.promises.readFile(oauthPath, 'utf-8');
const oauth = JSON.parse(oauthData);
if (oauth.refresh_token)
return true;
}
catch {
// OAuth not configured, check other methods
}
// Check for API key in settings.json
const settingsPath = path_1.default.join(geminiDir, 'settings.json');
try {
const settingsData = await fs_1.promises.readFile(settingsPath, 'utf-8');
const settings = JSON.parse(settingsData);
if (settings.apiKey)
return true;
// Check if OAuth is selected in settings
if (settings.security?.auth?.selectedType === 'oauth-personal')
return true;
}
catch {
// Settings not found
}
return false;
}
catch {
return false;
}
}
async function configureGemini() {
console.log('\n📋 Gemini Authentication Setup');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('\nGemini CLI requires interactive authentication.');
console.log('Please run the following command:\n');
console.log(' npx @google/gemini-cli@latest\n');
console.log('Then type: /auth');
console.log('\nYou can choose between:');
console.log(' • Google OAuth (recommended)');
console.log(' • Gemini API Key');
console.log(' • Vertex AI\n');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
const answer = await promptYesNo();
if (answer) {
console.log('Proceeding with setup (run gemini authentication separately)...\n');
}
else {
throw new Error('Gemini authentication skipped');
}
}
// Cursor
async function checkCursorAuth() {
return new Promise((resolve) => {
const child = (0, child_process_1.spawn)('cursor-agent', ['status'], {
stdio: 'pipe',
shell: false,
});
child.on('exit', (code) => resolve(code === 0));
child.on('error', () => resolve(false));
});
}
async function configureCursor() {
return new Promise((resolve, reject) => {
console.log(`\nLaunching Cursor Agent authentication...\n`);
const child = (0, child_process_1.spawn)('cursor-agent', ['login'], {
stdio: 'inherit',
shell: false,
});
child.on('exit', (code) => {
if (code === 0) {
console.log('\n✓ Cursor configured');
resolve();
}
else {
reject(new Error(`Cursor login failed with code ${code}`));
}
});
child.on('error', (error) => {
reject(new Error(`Failed to launch Cursor Agent: ${error.message}`));
});
});
}
// GitHub Copilot
async function checkCopilotAuth() {
// Copilot uses GitHub CLI auth
return new Promise((resolve) => {
const child = (0, child_process_1.spawn)('gh', ['auth', 'status'], {
stdio: 'pipe',
shell: false,
});
child.on('exit', (code) => resolve(code === 0));
child.on('error', () => resolve(false));
});
}
async function configureCopilot() {
return new Promise((resolve, reject) => {
console.log(`\nLaunching GitHub authentication for Copilot...\n`);
const child = (0, child_process_1.spawn)('npx', ['gh', 'auth', 'login'], {
stdio: 'inherit',
shell: false,
});
child.on('exit', (code) => {
if (code === 0) {
console.log('\n✓ GitHub Copilot configured (authenticated via GitHub CLI)');
resolve();
}
else {
reject(new Error(`GitHub auth failed with code ${code}`));
}
});
child.on('error', (error) => {
reject(new Error(`Failed to launch GitHub CLI: ${error.message}`));
});
});
}
// Qwen Code
async function checkQwenAuth() {
try {
// Qwen stores config in ~/.qwen/ directory
const settingsPath = path_1.default.join(os_1.default.homedir(), '.qwen', 'settings.json');
const settingsData = await fs_1.promises.readFile(settingsPath, 'utf-8');
const settings = JSON.parse(settingsData);
// Check for API key in settings
return !!settings.apiKey;
}
catch {
return false;
}
}
async function configureQwen() {
console.log('\n📋 Qwen Code Authentication Setup');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('\nQwen Code requires interactive authentication.');
console.log('Please run the following command:\n');
console.log(' npx @qwen-code/qwen-code@latest\n');
console.log('Then configure your API credentials through the CLI.\n');
console.log('You can use --openai-api-key flag or configure via settings.\n');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
const answer = await promptYesNo();
if (answer) {
console.log('Proceeding with setup (run qwen authentication separately)...\n');
}
else {
throw new Error('Qwen authentication skipped');
}
}
// Helper functions
function getExecutorName(executorId) {
const names = {
OPENCODE: 'OpenCode',
CLAUDE_CODE: 'Claude Code',
CODEX: 'Codex',
GEMINI: 'Gemini CLI',
CURSOR: 'Cursor',
COPILOT: 'GitHub Copilot',
QWEN_CODE: 'Qwen Code',
};
return names[executorId] || executorId;
}
function promptYesNo() {
return new Promise((resolve) => {
const rl = readline_1.default.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question('', (answer) => {
rl.close();
const normalized = answer.trim().toLowerCase();
resolve(normalized === '' || normalized === 'y' || normalized === 'yes');
});
});
}
function promptText(question) {
return new Promise((resolve) => {
const rl = readline_1.default.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question(question, (answer) => {
rl.close();
resolve(answer.trim());
});
});
}