UNPKG

@graphteon/juricode

Version:

We are forging the future with lines of digital steel

272 lines 10.7 kB
#!/usr/bin/env node "use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.setupVSCodeTunnelFromAPI = exports.setupVSCodeTunnel = void 0; const commander_1 = require("commander"); const prompts_1 = __importDefault(require("prompts")); const chalk_1 = __importDefault(require("chalk")); const figlet_1 = __importDefault(require("figlet")); const gradient_string_1 = __importDefault(require("gradient-string")); const ssh2_1 = require("ssh2"); const fs_1 = require("fs"); const path_1 = require("path"); const net_1 = require("net"); const task_1 = require("./commands/task"); const repository_1 = require("./commands/repository"); const suggested_tasks_1 = require("./commands/suggested-tasks"); const conversation_tui_1 = require("./commands/conversation-tui"); const getPackageVersion = () => { try { const possiblePaths = [ (0, path_1.join)(__dirname, '..', 'package.json'), (0, path_1.join)(__dirname, '..', '..', 'package.json'), (0, path_1.join)(process.cwd(), 'package.json'), ]; for (const packageJsonPath of possiblePaths) { if ((0, fs_1.existsSync)(packageJsonPath)) { const packageJson = JSON.parse((0, fs_1.readFileSync)(packageJsonPath, 'utf8')); return packageJson.version; } } return '0.3.1'; } catch (error) { return '0.3.1'; } }; let globalTunnelInfo = null; const setupVSCodeTunnel = async (vscodePort) => { if (!globalTunnelInfo) { throw new Error('Main tunnel not established'); } const { ssh, tunnels } = globalTunnelInfo; return new Promise((resolve, reject) => { const localServer = (0, net_1.createServer)((socket) => { ssh.forwardOut('127.0.0.1', socket.localPort || 0, '127.0.0.1', vscodePort, (err, stream) => { if (err) { console.error(chalk_1.default.red(`VSCode tunnel error: ${err.message}`)); socket.end(); return; } socket.pipe(stream).pipe(socket); socket.on('close', () => { stream.end(); }); stream.on('close', () => { socket.end(); }); }); }); localServer.listen(vscodePort, '127.0.0.1', () => { tunnels.set(vscodePort, localServer); resolve(); }); localServer.on('error', (err) => { reject(err); }); }); }; exports.setupVSCodeTunnel = setupVSCodeTunnel; const setupVSCodeTunnelFromAPI = async (taskId) => { if (!globalTunnelInfo) { throw new Error('Main tunnel not established'); } try { const response = await fetch(`http://localhost:13000/api/conversations/${taskId}/vscode-url`, { headers: { 'Accept': 'application/json, text/plain, */*', 'User-Agent': 'JuriCode CLI' } }); if (!response.ok) { throw new Error(`API request failed: ${response.status} ${response.statusText}`); } const data = await response.json(); const vscodeUrl = data.vscode_url; if (!vscodeUrl) { throw new Error('No VSCode URL received from API'); } const urlMatch = vscodeUrl.match(/localhost:(\d+)/); if (!urlMatch) { throw new Error('Could not parse port from VSCode URL'); } const vscodePort = parseInt(urlMatch[1]); await setupVSCodeTunnel(vscodePort); const tunneledUrl = vscodeUrl.replace(`localhost:${vscodePort}`, `localhost:${vscodePort}`); return tunneledUrl; } catch (error) { console.error(chalk_1.default.red(`Failed to setup VSCode tunnel: ${error instanceof Error ? error.message : 'Unknown error'}`)); throw error; } }; exports.setupVSCodeTunnelFromAPI = setupVSCodeTunnelFromAPI; const setupTunnel = async (target) => { let user, host, port = '3000'; if (target.includes(':')) { [target, port] = target.split(':'); } [user, host] = target.split('@'); if (!user || !host) { throw new Error('Invalid target format. Use <user>@<host> or <user>@<host>:<port>'); } console.log(chalk_1.default.yellow(`Setting up SSH connection to ${user}@${host}...`)); const ssh = new ssh2_1.Client(); return new Promise((resolve, reject) => { const keyPath = `${process.env.HOME}/.ssh/id_rsa`; if (!(0, fs_1.existsSync)(keyPath)) { reject(new Error(`SSH key not found: ${keyPath}`)); return; } try { const privateKey = (0, fs_1.readFileSync)(keyPath, 'utf8'); console.log(chalk_1.default.yellow(`Using SSH key: ${keyPath}`)); ssh.on('ready', () => { console.log(chalk_1.default.green('SSH connection established!')); const localServer = (0, net_1.createServer)((socket) => { ssh.forwardOut('127.0.0.1', socket.localPort || 0, '127.0.0.1', parseInt(port), (err, stream) => { if (err) { console.error(chalk_1.default.red(`Tunnel error: ${err.message}`)); socket.end(); return; } socket.pipe(stream).pipe(socket); socket.on('close', () => { stream.end(); }); stream.on('close', () => { socket.end(); }); }); }); localServer.listen(13000, '127.0.0.1', () => { console.log(chalk_1.default.green('Main SSH tunnel established!')); globalTunnelInfo = { user, host, apiPort: port, ssh, tunnels: new Map([[13000, localServer]]) }; resolve(); }); localServer.on('error', (err) => { console.error(chalk_1.default.red(`Local server error: ${err.message}`)); reject(err); }); }).on('error', (err) => { console.error(chalk_1.default.red(`SSH connection error: ${err.message}`)); reject(err); }); ssh.connect({ host: host, username: user, privateKey: privateKey, readyTimeout: 20000, algorithms: { kex: ['diffie-hellman-group14-sha256', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512'], cipher: ['aes128-ctr', 'aes192-ctr', 'aes256-ctr'], hmac: ['hmac-sha2-256', 'hmac-sha2-512'], compress: ['none'] } }); } catch (error) { reject(new Error(`Failed to read SSH key: ${error instanceof Error ? error.message : 'Unknown error'}`)); } process.on('SIGINT', () => { console.log(chalk_1.default.yellow('\nClosing SSH connection...')); ssh.end(); process.exit(); }); process.on('SIGTERM', () => { console.log(chalk_1.default.yellow('\nClosing SSH connection...')); ssh.end(); process.exit(); }); }); }; const version = getPackageVersion(); console.log(gradient_string_1.default.pastel.multiline(figlet_1.default.textSync('JuriCode', { font: 'Big', horizontalLayout: 'default', verticalLayout: 'default', }))); console.log(chalk_1.default.dim(`v${version}\n`)); const startTUIInterface = async () => { console.log(chalk_1.default.green('🚀 Starting JuriCode TUI Interface...\n')); await (0, conversation_tui_1.startConversationTUI)(); }; const showLegacyMenu = async () => { const { action } = await (0, prompts_1.default)({ type: 'select', name: 'action', message: 'What would you like to do?', choices: [ { title: '📋 View All Tasks (CLI)', value: 'view-tasks' }, { title: '💡 Suggested Tasks', value: 'suggested-tasks' }, { title: '📚 Browse Repositories', value: 'view-repos' }, { title: '📝 Create New Task', value: 'new-task' }, { title: '💬 Start TUI Conversation', value: 'tui-conversation' }, { title: '🚪 Exit', value: 'exit' } ] }); switch (action) { case 'new-task': await (0, task_1.createNewTask)(); break; case 'view-tasks': await (0, task_1.listTasks)(); break; case 'suggested-tasks': await (0, suggested_tasks_1.listSuggestedTasks)(); break; case 'view-repos': await (0, repository_1.listRepositories)(); break; case 'tui-conversation': const { taskId } = await (0, prompts_1.default)({ type: 'text', name: 'taskId', message: 'Enter Task ID for conversation:', validate: (input) => input.length === 0 ? 'Task ID is required' : true }); if (taskId) { await (0, conversation_tui_1.startConversationTUI)(taskId); } break; case 'exit': console.log(chalk_1.default.yellow('👋 Goodbye!')); process.exit(0); } await showLegacyMenu(); }; const program = new commander_1.Command(); program .name('juricode') .description('JuriCode CLI') .version(getPackageVersion()) .option('-t, --tunnel <target>', 'Setup SSH tunnel (format: <user>@<host> or <user>@<host>:<port>, default port: 13000)') .option('--legacy', 'Use legacy CLI menu instead of TUI interface') .action(async (options) => { try { if (options.tunnel) { await setupTunnel(options.tunnel); } if (options.legacy) { await showLegacyMenu(); } else { await startTUIInterface(); } } catch (error) { console.error(chalk_1.default.red(error instanceof Error ? error.message : 'Unknown error')); process.exit(1); } }); program.parse(process.argv); //# sourceMappingURL=index.js.map