@graphteon/juricode
Version:
We are forging the future with lines of digital steel
156 lines • 6.35 kB
JavaScript
import * as p from '@clack/prompts';
import chalk from 'chalk';
import { WSClient } from '../api/ws-client';
import { formatMessage } from '../utils/format-message';
import { setupVSCodeTunnel } from '../index';
import OpenHands from '../api/open-hands';
const getEditorUrl = async (conversationId) => {
try {
const response = await OpenHands.getVSCodeUrl(conversationId);
if (response.error) {
throw new Error(response.error);
}
if (!response.vscode_url) {
throw new Error('No VSCode URL available');
}
const vscodeUrl = new URL(response.vscode_url);
const vscodePort = parseInt(vscodeUrl.port);
try {
await setupVSCodeTunnel(vscodePort);
}
catch (error) {
console.log(chalk.yellow('Note: VSCode tunnel setup failed. You may need to run:'));
console.log(chalk.cyan(`ssh -L ${vscodePort}:127.0.0.1:${vscodePort} \${USER}@\${HOST}`));
}
return response.vscode_url;
}
catch (error) {
throw new Error(`Failed to get editor URL: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
};
const formatCodeBlock = (code, language) => {
const lines = code.trim().split('\n');
const maxLength = Math.max(...lines.map(line => line.length));
const border = '─'.repeat(maxLength + 2);
const langHeader = language ? `${chalk.yellow(`[${language}]`)}\n` : '';
return '\n' + chalk.dim('┌' + border + '┐') + '\n' +
langHeader +
lines.map(line => chalk.dim('│') + ' ' + chalk.cyan(line) + ' '.repeat(maxLength - line.length) + chalk.dim(' │')).join('\n') + '\n' +
chalk.dim('└' + border + '┘') + '\n';
};
const formatContent = (text) => {
text = text.replace(/```(\w+)?\n([\s\S]*?)```/g, (_, lang, code) => formatCodeBlock(code, lang));
text = text.replace(/`([^`]+)`/g, (_, code) => chalk.cyan(code));
text = text.replace(/\*\*([^*]+)\*\*/g, (_, content) => chalk.bold(content));
text = text.replace(/\*([^*]+)\*/g, (_, content) => chalk.italic(content));
text = text.replace(/^- (.+)$/gm, (_, content) => '• ' + content);
text = text.replace(/^(\d+)\. (.+)$/gm, (_, num, content) => `${num}. ${content}`);
text = text.replace(/^> (.+)$/gm, (_, content) => chalk.dim('│ ') + chalk.italic(content));
return text;
};
export const runTaskChat = async (taskId) => {
p.intro('Starting chat session');
const s = p.spinner();
let isSpinning = true;
s.start('Connecting to chat');
const ws = new WSClient();
let isAgentReady = false;
let isConnected = false;
try {
ws.onConnect(() => {
isConnected = true;
s.stop('🚀 Connected to chat');
isSpinning = false;
if (!isAgentReady) {
s.start('Agent is processing');
isSpinning = true;
}
});
ws.onMessage(async (event) => {
if (isSpinning) {
s.stop('⛵🚀🚀🚀🚀');
isSpinning = false;
}
const formatted = formatMessage(event);
const timestamp = new Date(event.timestamp).toLocaleTimeString();
if (event.source === 'user') {
p.note(formatContent(formatted.text), `${chalk.blue('🧔 You')} ${chalk.dim(timestamp)}`);
}
else if (event.source === 'agent') {
p.note(formatContent(formatted.text), `${chalk.green('🤖 Assistant')} ${chalk.dim(timestamp)}`);
}
else {
p.note(formatContent(formatted.text), `${chalk.yellow('💻 System')} ${chalk.dim(timestamp)}`);
}
});
ws.onError((error) => {
if (isSpinning) {
s.stop();
isSpinning = false;
}
p.cancel(`Chat error: ${error.message}`);
});
ws.onStateChange((state) => {
if (state === 'awaiting_user_input' || state === 'finished') {
isAgentReady = true;
if (isSpinning) {
s.stop(state === 'finished' ? '✅ Agent finished' : '⛵ Ready for input');
isSpinning = false;
}
}
else {
isAgentReady = false;
if (isConnected && !isSpinning) {
s.start('Agent is processing');
isSpinning = true;
}
}
});
await ws.connect(taskId);
while (true) {
while (!isAgentReady) {
await new Promise(resolve => setTimeout(resolve, 100));
}
try {
const message = await p.text({
message: chalk.blue('You:'),
validate: input => input.length === 0 ? 'Message cannot be empty' : undefined,
placeholder: 'Type /editor to get editor link'
});
if (p.isCancel(message) || !message || message.toLowerCase() === 'exit') {
break;
}
if (message === '/editor') {
try {
const url = await getEditorUrl(taskId);
console.log('\n' + chalk.green('✨ Editor URL: ') + chalk.blue.underline(url) + '\n');
continue;
}
catch (error) {
p.log.error('Failed to get editor URL');
continue;
}
}
ws.send(message);
isAgentReady = false;
s.start('Agent is processing');
isSpinning = true;
}
catch (error) {
p.log.error('Failed to send message');
p.log.error(error instanceof Error ? error.message : 'Unknown error');
}
}
ws.disconnect();
p.outro('Chat ended');
}
catch (error) {
if (isSpinning) {
s.stop('Failed to start chat');
isSpinning = false;
}
p.log.error('Failed to start chat');
p.log.error(error instanceof Error ? error.message : 'Unknown error');
}
};
//# sourceMappingURL=chat.js.map