@graphteon/juricode
Version:
We are forging the future with lines of digital steel
272 lines • 10.7 kB
JavaScript
;
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