';
}
// ========== File Browser Modal ==========
var fileBrowserState = {
currentPath: '',
showHidden: false,
onSelect: null
};
function showFileBrowserModal(onSelect) {
fileBrowserState.onSelect = onSelect;
fileBrowserState.showHidden = false;
// Create modal overlay
var overlay = document.createElement('div');
overlay.id = 'fileBrowserOverlay';
overlay.className = 'modal-overlay';
overlay.innerHTML = buildFileBrowserModalContent();
document.body.appendChild(overlay);
// Load initial directory (home)
loadFileBrowserDirectory('');
// Initialize events
initFileBrowserEvents();
// Initialize icons
if (window.lucide) lucide.createIcons();
}
function buildFileBrowserModalContent() {
// Detect if Windows
var isWindows = navigator.platform.indexOf('Win') > -1;
var driveButtons = '';
if (isWindows) {
driveButtons = '
' +
'' +
'' +
'' +
'
';
}
return '
' +
'
' +
'
' + t('cli.fileBrowser') + '
' +
'' +
'
' +
'
' +
'
' +
'' +
'' +
driveButtons +
'' +
'' +
'
' +
'
' +
'
' +
'
' +
'
' +
'' +
'
';
}
async function loadFileBrowserDirectory(path) {
var listContainer = document.getElementById('fileBrowserList');
var pathInput = document.getElementById('fileBrowserPathInput');
if (listContainer) {
listContainer.innerHTML = '
';
if (window.lucide) lucide.createIcons();
}
try {
var response = await fetch('/api/dialog/browse', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ path: path, showHidden: fileBrowserState.showHidden })
});
if (!response.ok) {
throw new Error('Failed to load directory');
}
var data = await response.json();
fileBrowserState.currentPath = data.currentPath;
if (pathInput) {
pathInput.value = data.currentPath;
}
renderFileBrowserItems(data.items);
} catch (err) {
console.error('Failed to load directory:', err);
if (listContainer) {
listContainer.innerHTML = '
' +
'
' + t('cli.fileBrowserApiError') + '
' +
'
' + t('cli.fileBrowserManualHint') + '
' +
'
';
}
// Enable manual path entry mode - enable select button when path is typed
var selectBtn = document.getElementById('fileBrowserSelectBtn');
var pathInput = document.getElementById('fileBrowserPathInput');
if (selectBtn && pathInput) {
selectBtn.disabled = false;
pathInput.focus();
}
}
}
function renderFileBrowserItems(items) {
var listContainer = document.getElementById('fileBrowserList');
if (!listContainer) return;
if (!items || items.length === 0) {
listContainer.innerHTML = '
Empty directory
';
return;
}
var html = items.map(function(item) {
var icon = item.isDirectory ? 'folder' : 'file';
var itemClass = 'file-browser-item' + (item.isDirectory ? ' is-directory' : ' is-file');
return '
' +
'' +
'' + escapeHtml(item.name) + '' +
'
';
}).join('');
listContainer.innerHTML = html;
// Initialize icons
if (window.lucide) lucide.createIcons();
// Add click handlers
listContainer.querySelectorAll('.file-browser-item').forEach(function(el) {
el.onclick = function() {
var isDir = el.getAttribute('data-is-dir') === 'true';
var path = el.getAttribute('data-path');
if (isDir) {
// Navigate into directory
loadFileBrowserDirectory(path);
} else {
// Select file
listContainer.querySelectorAll('.file-browser-item').forEach(function(item) {
item.classList.remove('selected');
});
el.classList.add('selected');
// Enable select button
var selectBtn = document.getElementById('fileBrowserSelectBtn');
if (selectBtn) {
selectBtn.disabled = false;
selectBtn.setAttribute('data-selected-path', path);
}
}
};
// Double-click to select file or enter directory
el.ondblclick = function() {
var isDir = el.getAttribute('data-is-dir') === 'true';
var path = el.getAttribute('data-path');
if (isDir) {
loadFileBrowserDirectory(path);
} else {
// Select and close
closeFileBrowserModal(path);
}
};
});
}
function initFileBrowserEvents() {
// Close button
var closeBtn = document.getElementById('fileBrowserCloseBtn');
if (closeBtn) {
closeBtn.onclick = function() { closeFileBrowserModal(null); };
}
// Cancel button
var cancelBtn = document.getElementById('fileBrowserCancelBtn');
if (cancelBtn) {
cancelBtn.onclick = function() { closeFileBrowserModal(null); };
}
// Select button
var selectBtn = document.getElementById('fileBrowserSelectBtn');
if (selectBtn) {
selectBtn.onclick = function() {
// First try selected path from list, then fall back to path input
var path = selectBtn.getAttribute('data-selected-path');
if (!path) {
var pathInput = document.getElementById('fileBrowserPathInput');
if (pathInput && pathInput.value.trim()) {
path = pathInput.value.trim();
}
}
if (path) {
closeFileBrowserModal(path);
}
};
}
// Up button
var upBtn = document.getElementById('fileBrowserUpBtn');
if (upBtn) {
upBtn.onclick = function() {
// Get parent path
var currentPath = fileBrowserState.currentPath;
var parentPath = currentPath.replace(/[/\\][^/\\]+$/, '') || '/';
loadFileBrowserDirectory(parentPath);
};
}
// Home button
var homeBtn = document.getElementById('fileBrowserHomeBtn');
if (homeBtn) {
homeBtn.onclick = function() {
loadFileBrowserDirectory('');
};
}
// Drive buttons (Windows)
document.querySelectorAll('.drive-btn').forEach(function(btn) {
btn.onclick = function() {
var drive = btn.getAttribute('data-drive');
if (drive) {
loadFileBrowserDirectory(drive);
}
};
});
// Path input - allow manual entry
var pathInput = document.getElementById('fileBrowserPathInput');
if (pathInput) {
pathInput.onkeydown = function(e) {
if (e.key === 'Enter') {
e.preventDefault();
var path = pathInput.value.trim();
if (path) {
loadFileBrowserDirectory(path);
}
}
};
}
// Show hidden checkbox
var showHiddenCheckbox = document.getElementById('fileBrowserShowHidden');
if (showHiddenCheckbox) {
showHiddenCheckbox.checked = true; // Default to show hidden
fileBrowserState.showHidden = true;
showHiddenCheckbox.onchange = function() {
fileBrowserState.showHidden = showHiddenCheckbox.checked;
loadFileBrowserDirectory(fileBrowserState.currentPath);
};
}
// Click outside to close
var overlay = document.getElementById('fileBrowserOverlay');
if (overlay) {
overlay.onclick = function(e) {
if (e.target === overlay) {
closeFileBrowserModal(null);
}
};
}
}
function closeFileBrowserModal(selectedPath) {
var overlay = document.getElementById('fileBrowserOverlay');
if (overlay) {
overlay.remove();
}
if (fileBrowserState.onSelect && selectedPath) {
fileBrowserState.onSelect(selectedPath);
}
fileBrowserState.onSelect = null;
}
function initToolConfigModalEvents(tool, currentConfig, models) {
// Local tags state (copy from config)
var currentTags = (currentConfig.tags || []).slice();
// Helper to render tags inline with input
function renderTags() {
var container = document.getElementById('tagsUnifiedInput');
var input = document.getElementById('tagInput');
if (!container) return;
// Remove existing tag items but keep the input
container.querySelectorAll('.tag-item').forEach(function(el) { el.remove(); });
// Insert tags before the input
currentTags.forEach(function(tag) {
var tagEl = document.createElement('span');
tagEl.className = 'tag-item tag-' + escapeHtml(tag);
tagEl.innerHTML = escapeHtml(tag) + '';
container.insertBefore(tagEl, input);
});
// Re-attach remove handlers
container.querySelectorAll('.tag-remove').forEach(function(btn) {
btn.onclick = function(e) {
e.stopPropagation();
var tagToRemove = this.getAttribute('data-tag');
currentTags = currentTags.filter(function(t) { return t !== tagToRemove; });
renderTags();
};
});
// Update predefined tag buttons state
document.querySelectorAll('.predefined-tag-btn').forEach(function(btn) {
var tag = btn.getAttribute('data-tag');
if (currentTags.indexOf(tag) !== -1) {
btn.classList.add('selected');
btn.disabled = true;
} else {
btn.classList.remove('selected');
btn.disabled = false;
}
});
}
// Click on unified input container focuses the input
var unifiedInput = document.getElementById('tagsUnifiedInput');
if (unifiedInput) {
unifiedInput.onclick = function(e) {
if (e.target === this) {
document.getElementById('tagInput').focus();
}
};
}
// Tag input handler
var tagInput = document.getElementById('tagInput');
if (tagInput) {
tagInput.onkeydown = function(e) {
if (e.key === 'Enter') {
e.preventDefault();
var newTag = this.value.trim();
if (newTag && currentTags.indexOf(newTag) === -1) {
currentTags.push(newTag);
renderTags();
}
this.value = '';
}
};
}
// Predefined tag click handlers
document.querySelectorAll('.predefined-tag-btn').forEach(function(btn) {
btn.onclick = function() {
var tag = this.getAttribute('data-tag');
if (tag && currentTags.indexOf(tag) === -1) {
currentTags.push(tag);
renderTags();
}
};
});
// Initialize tags display
renderTags();
// Initialize lucide icons for predefined buttons
if (window.lucide) lucide.createIcons();
// Toggle Enable/Disable
var toggleBtn = document.getElementById('toggleEnableBtn');
if (toggleBtn) {
toggleBtn.onclick = async function() {
var newEnabled = !currentConfig.enabled;
try {
await updateCliToolConfig(tool, { enabled: newEnabled });
showRefreshToast(tool + ' ' + (newEnabled ? 'enabled' : 'disabled'), 'success');
closeModal();
renderToolsSection();
if (window.lucide) lucide.createIcons();
} catch (err) {
showRefreshToast('Failed to update: ' + err.message, 'error');
}
};
}
// Install/Uninstall
var installBtn = document.getElementById('installBtn');
if (installBtn) {
installBtn.onclick = function() {
var status = cliToolStatus[tool] || {};
closeModal();
if (status.available) {
openCliUninstallWizard(tool);
} else {
openCliInstallWizard(tool);
}
};
}
// Model select handlers
var primarySelect = document.getElementById('primaryModelSelect');
var primaryCustom = document.getElementById('primaryModelCustom');
var secondarySelect = document.getElementById('secondaryModelSelect');
var secondaryCustom = document.getElementById('secondaryModelCustom');
if (primarySelect && primaryCustom) {
primarySelect.onchange = function() {
if (this.value === '__custom__') {
primaryCustom.style.display = 'block';
primaryCustom.focus();
} else {
primaryCustom.style.display = 'none';
primaryCustom.value = '';
}
};
}
if (secondarySelect && secondaryCustom) {
secondarySelect.onchange = function() {
if (this.value === '__custom__') {
secondaryCustom.style.display = 'block';
secondaryCustom.focus();
} else {
secondaryCustom.style.display = 'none';
secondaryCustom.value = '';
}
};
}
// Save button
var saveBtn = document.getElementById('saveConfigBtn');
if (saveBtn) {
saveBtn.onclick = async function() {
var primaryModel = primarySelect.value === '__custom__'
? primaryCustom.value.trim()
: primarySelect.value;
var secondaryModel = secondarySelect.value === '__custom__'
? secondaryCustom.value.trim()
: secondarySelect.value;
if (!primaryModel) {
showRefreshToast('Primary model is required', 'error');
return;
}
if (!secondaryModel) {
showRefreshToast('Secondary model is required', 'error');
return;
}
// Get envFile value (only for gemini/qwen)
var envFileInput = document.getElementById('envFileInput');
var envFile = envFileInput ? envFileInput.value.trim() : '';
try {
var updateData = {
primaryModel: primaryModel,
secondaryModel: secondaryModel,
tags: currentTags
};
// Only include envFile for gemini/qwen tools
if (tool === 'gemini' || tool === 'qwen') {
updateData.envFile = envFile || null;
}
await updateCliToolConfig(tool, updateData);
// Reload config to reflect changes
await loadCliToolConfig();
showRefreshToast('Configuration saved', 'success');
closeModal();
renderToolsSection();
if (window.lucide) lucide.createIcons();
} catch (err) {
showRefreshToast('Failed to save: ' + err.message, 'error');
}
};
}
// Environment file browse button (only for gemini/qwen)
var envFileBrowseBtn = document.getElementById('envFileBrowseBtn');
if (envFileBrowseBtn) {
envFileBrowseBtn.onclick = function() {
showFileBrowserModal(function(selectedPath) {
var envFileInput = document.getElementById('envFileInput');
if (envFileInput && selectedPath) {
envFileInput.value = selectedPath;
envFileInput.focus();
}
});
};
}
// Initialize lucide icons in modal
if (window.lucide) lucide.createIcons();
}
// ========== Rendering ==========
/**
* 构建 CLI Manager 骨架屏
* @returns {string} HTML 字符串
*/
function buildCliManagerSkeleton() {
return '
';
}).join('');
// CodexLens and Semantic Search removed from this list
// They are managed in the dedicated CodexLens Manager page (left menu)
// API Endpoints section
var apiEndpointsHtml = '';
if (litellmApiEndpoints.length > 0) {
var endpointItems = litellmApiEndpoints.map(function(endpoint) {
// Check if endpoint is synced to CLI tools
var cliEndpoint = cliCustomEndpoints.find(function(e) { return e.id === endpoint.id; });
var isSynced = !!cliEndpoint;
var isEnabled = cliEndpoint ? cliEndpoint.enabled : false;
// Find provider info
var provider = (window.litellmApiConfig?.providers || []).find(function(p) { return p.id === endpoint.providerId; });
var providerName = provider ? provider.name : endpoint.providerId;
return '
';
}
// CLI Wrapper (CLI封装) section
var cliWrapperHtml = '';
if (cliWrapperEndpoints.length > 0) {
var wrapperItems = cliWrapperEndpoints.map(function(endpoint) {
var isEnabled = endpoint.enabled !== false;
var desc = endpoint.description || (t('cli.customClaudeSettings') || 'Custom Claude CLI settings');
// Show command hint with name for easy copying
var commandHint = 'ccw cli --tool ' + endpoint.name;
return '
' +
apiEndpointsHtml +
cliWrapperHtml;
if (window.lucide) lucide.createIcons();
}
/**
* Navigate to API Settings page and open the CLI wrapper endpoint for editing
*/
function navigateToApiSettings(endpointId) {
// Store the endpoint ID to edit after navigation
window.pendingCliWrapperEdit = endpointId;
var navItem = document.querySelector('.nav-item[data-view="api-settings"]');
if (navItem) {
navItem.click();
} else {
// Fallback: try to render directly
if (typeof renderApiSettings === 'function') {
currentView = 'api-settings';
renderApiSettings();
} else {
showRefreshToast(t('common.error') + ': API Settings not available', 'error');
}
}
}
// ========== CCW Section (Right Column) ==========
function renderCcwSection() {
var container = document.getElementById('ccw-section');
if (!container) return;
var installationsHtml = '';
if (ccwInstallations.length === 0) {
installationsHtml = '
' +
'' +
'
' + t('ccw.noInstallations') + '
' +
'' +
'
';
} else {
installationsHtml = '
';
for (var i = 0; i < ccwInstallations.length; i++) {
var inst = ccwInstallations[i];
var isGlobal = inst.installation_mode === 'Global';
var modeIcon = isGlobal ? 'home' : 'folder';
var version = inst.application_version || 'unknown';
var installDate = new Date(inst.installation_date).toLocaleDateString();
installationsHtml += '
';
}
// Usage example
var usageExample = 'ccw tool exec ' + name;
if (propKeys.length > 0) {
var exampleParams = {};
for (var j = 0; j < Math.min(propKeys.length, 2); j++) {
var k = propKeys[j];
var p = properties[k];
if (p.type === 'string') exampleParams[k] = '';
else if (p.type === 'boolean') exampleParams[k] = true;
else if (p.type === 'number') exampleParams[k] = 0;
else exampleParams[k] = '';
}
usageExample += " '" + JSON.stringify(exampleParams) + "'";
}
var modalContent = '
' +
'
' +
'
' +
'
' +
'
' + escapeHtml(name) + '
' +
'endpoint tool' +
'
' +
'
' +
'
' + escapeHtml(desc) + '
' +
paramsHtml +
'
' +
'
Usage Example
' +
'
' +
'' + escapeHtml(usageExample) + '' +
'' +
'
' +
'
' +
'
';
showModal(name, modalContent, { size: 'lg' });
}
function copyToolUsage(btn, text) {
navigator.clipboard.writeText(text).then(function() {
var icon = btn.querySelector('i');
if (icon) {
icon.setAttribute('data-lucide', 'check');
if (window.lucide) lucide.createIcons();
setTimeout(function() {
icon.setAttribute('data-lucide', 'copy');
if (window.lucide) lucide.createIcons();
}, 2000);
}
});
}
// CCW Install Carousel State
var ccwCarouselIndex = 0;
function renderCcwInstallPanel() {
var container = document.getElementById('ccw-install-panel');
if (!container) return;
var html = '
CCW Installations
' +
'
' +
'' +
'' +
'
' +
'
';
if (ccwInstallations.length === 0) {
html += '
' +
'' +
'
No installations found
' +
'
';
} else {
// Carousel container
html += '
';
// Left arrow (show only if more than 1 installation)
if (ccwInstallations.length > 1) {
html += '';
}
html += '
';
for (var i = 0; i < ccwInstallations.length; i++) {
var inst = ccwInstallations[i];
var isGlobal = inst.installation_mode === 'Global';
var modeIcon = isGlobal ? 'home' : 'folder';
var version = inst.application_version || 'unknown';
var installDate = new Date(inst.installation_date).toLocaleDateString();
var activeClass = i === ccwCarouselIndex ? 'active' : '';
html += '
';
// Right arrow (show only if more than 1 installation)
if (ccwInstallations.length > 1) {
html += '';
}
html += '
';
// Dots indicator (show only if more than 1 installation)
if (ccwInstallations.length > 1) {
html += '
';
for (var j = 0; j < ccwInstallations.length; j++) {
var dotActive = j === ccwCarouselIndex ? 'active' : '';
html += '';
}
html += '
';
}
}
html += '
';
container.innerHTML = html;
if (window.lucide) lucide.createIcons();
// Update carousel position
updateCcwCarouselPosition();
}
function ccwCarouselPrev() {
if (ccwCarouselIndex > 0) {
ccwCarouselIndex--;
updateCcwCarouselPosition();
updateCcwCarouselDots();
}
}
function ccwCarouselNext() {
if (ccwCarouselIndex < ccwInstallations.length - 1) {
ccwCarouselIndex++;
updateCcwCarouselPosition();
updateCcwCarouselDots();
}
}
function ccwCarouselGoTo(index) {
ccwCarouselIndex = index;
updateCcwCarouselPosition();
updateCcwCarouselDots();
}
function updateCcwCarouselPosition() {
var track = document.getElementById('ccwCarouselTrack');
if (track) {
track.style.transform = 'translateX(-' + (ccwCarouselIndex * 100) + '%)';
}
// Update card active states
var cards = document.querySelectorAll('.ccw-carousel-card');
cards.forEach(function(card, idx) {
card.classList.toggle('active', idx === ccwCarouselIndex);
});
}
function updateCcwCarouselDots() {
var dots = document.querySelectorAll('.ccw-carousel-dot');
dots.forEach(function(dot, idx) {
dot.classList.toggle('active', idx === ccwCarouselIndex);
});
}
// CCW Install Modal
function showCcwInstallModal() {
var modalContent = '
' +
'
' +
'
' +
'
' +
'
' +
'
Global Installation
' +
'
Install to user home directory (~/.claude)
' +
'
' +
'' +
'
' +
'
' +
'
' +
'
' +
'
Path Installation
' +
'
Install to a specific project folder
' +
'
' +
'' +
'
' +
'
' +
'
' +
'
' +
'' +
'' +
'
' +
'
' +
'' +
'
' +
'
' +
'
';
showModal('Install CCW', modalContent);
}
function selectCcwInstallMode(mode) {
if (mode === 'Global') {
closeModal();
runCcwInstall('Global');
}
}
function toggleCcwPathInput() {
var section = document.getElementById('ccwPathInputSection');
if (section) {
section.classList.toggle('hidden');
if (!section.classList.contains('hidden')) {
var input = document.getElementById('ccwInstallPath');
if (input) input.focus();
}
}
}
function executeCcwInstall() {
var input = document.getElementById('ccwInstallPath');
var path = input ? input.value.trim() : '';
if (!path) {
showRefreshToast('Please enter a path', 'error');
return;
}
closeModal();
runCcwInstall('Path', path);
}
function truncatePath(path) {
if (!path) return '';
var maxLen = 35;
if (path.length <= maxLen) return path;
return '...' + path.slice(-maxLen + 3);
}
function renderCliExecutePanel() {
var container = document.getElementById('cli-execute-panel');
if (!container) return;
var tools = ['gemini', 'qwen', 'codex'];
var modes = ['analysis', 'write', 'auto'];
var html = '
Quick Execute
' +
'
' +
'
' +
'
' +
'
' +
'
' +
'
' +
'
' +
'
' +
'
';
container.innerHTML = html;
if (window.lucide) lucide.createIcons();
}
// ========== CCW Actions ==========
function runCcwInstall(mode, customPath) {
var command;
if (mode === 'Global') {
command = 'ccw install --mode Global';
} else {
var installPath = customPath || projectPath;
command = 'ccw install --mode Path --path "' + installPath + '"';
}
// Copy command to clipboard
if (navigator.clipboard) {
navigator.clipboard.writeText(command).then(function() {
showRefreshToast('Command copied: ' + command, 'success');
}).catch(function() {
showRefreshToast('Run: ' + command, 'info');
});
} else {
showRefreshToast('Run: ' + command, 'info');
}
}
async function runCcwUpgrade() {
showRefreshToast(t('ccw.upgradeStarting'), 'info');
try {
var response = await fetch('/api/ccw/upgrade', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({})
});
var result = await response.json();
if (result.success) {
showRefreshToast(t('ccw.upgradeCompleted'), 'success');
// Reload installations after upgrade
setTimeout(function() {
loadCcwInstallations().then(function() {
renderCcwInstallPanel();
});
}, 1000);
} else {
showRefreshToast(t('ccw.upgradeFailed', { error: result.error || 'Unknown error' }), 'error');
}
} catch (err) {
showRefreshToast(t('ccw.upgradeFailed', { error: err.message }), 'error');
}
}
function confirmCcwUninstall(installPath) {
if (confirm(t('ccw.uninstallConfirm') + '\n' + (installPath || 'Current installation'))) {
var command = installPath
? 'ccw uninstall --path "' + installPath + '"'
: 'ccw uninstall';
if (navigator.clipboard) {
navigator.clipboard.writeText(command).then(function() {
showRefreshToast('Command copied: ' + command, 'success');
}).catch(function() {
showRefreshToast('Run: ' + command, 'info');
});
} else {
showRefreshToast('Run: ' + command, 'info');
}
}
}
// ========== Execution ==========
async function executeCliFromDashboard() {
var toolEl = document.getElementById('cli-exec-tool');
var modeEl = document.getElementById('cli-exec-mode');
var promptEl = document.getElementById('cli-exec-prompt');
var tool = toolEl ? toolEl.value : 'gemini';
var mode = modeEl ? modeEl.value : 'analysis';
var prompt = promptEl ? promptEl.value.trim() : '';
if (!prompt) {
showRefreshToast(t('toast.enterPrompt'), 'error');
return;
}
currentCliExecution = { tool: tool, mode: mode, prompt: prompt, startTime: Date.now() };
cliExecutionOutput = '';
var outputPanel = document.getElementById('cli-output-panel');
var outputContent = document.getElementById('cli-output-content');
var statusIndicator = document.getElementById('cli-output-status-indicator');
var statusText = document.getElementById('cli-output-status-text');
if (outputPanel) outputPanel.classList.remove('hidden');
if (outputContent) outputContent.textContent = '';
if (statusIndicator) statusIndicator.className = 'status-indicator running';
if (statusText) statusText.textContent = 'Running...';
var execBtn = document.querySelector('.cli-execute-actions .btn-primary');
if (execBtn) execBtn.disabled = true;
try {
var response = await fetch('/api/cli/execute', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
tool: tool,
mode: mode,
prompt: prompt,
dir: projectPath,
format: promptConcatFormat,
smartContext: {
enabled: smartContextEnabled,
maxFiles: smartContextMaxFiles
}
})
});
var result = await response.json();
if (statusIndicator) statusIndicator.className = 'status-indicator ' + (result.success ? 'success' : 'error');
if (statusText) {
var duration = formatDuration(result.execution ? result.execution.duration_ms : (Date.now() - currentCliExecution.startTime));
statusText.textContent = result.success ? 'Completed in ' + duration : 'Failed: ' + (result.error || 'Unknown');
}
await loadCliHistory();
renderCliHistory();
showRefreshToast(result.success ? t('toast.completed') : (result.error || t('toast.failed')), result.success ? 'success' : 'error');
} catch (error) {
if (statusIndicator) statusIndicator.className = 'status-indicator error';
if (statusText) statusText.textContent = 'Error: ' + error.message;
showRefreshToast(t('toast.error', { error: error.message }), 'error');
}
currentCliExecution = null;
if (execBtn) execBtn.disabled = false;
}
// ========== WebSocket Event Handlers ==========
function handleCliExecutionStarted(payload) {
currentCliExecution = {
executionId: payload.executionId,
tool: payload.tool,
mode: payload.mode,
startTime: new Date(payload.timestamp).getTime()
};
cliExecutionOutput = '';
// Show toast notification
if (typeof addGlobalNotification === 'function') {
addGlobalNotification('info', 'CLI ' + payload.tool + ' started', payload.mode + ' mode', 'CLI');
}
if (currentView === 'cli-manager') {
var outputPanel = document.getElementById('cli-output-panel');
var outputContent = document.getElementById('cli-output-content');
var statusIndicator = document.getElementById('cli-output-status-indicator');
var statusText = document.getElementById('cli-output-status-text');
if (outputPanel) outputPanel.classList.remove('hidden');
if (outputContent) outputContent.textContent = '';
if (statusIndicator) statusIndicator.className = 'status-indicator running';
if (statusText) statusText.textContent = 'Running ' + payload.tool + ' (' + payload.mode + ')...';
}
}
function handleCliOutput(payload) {
cliExecutionOutput += payload.data;
var outputContent = document.getElementById('cli-output-content');
if (outputContent) {
outputContent.textContent = cliExecutionOutput;
outputContent.scrollTop = outputContent.scrollHeight;
}
}
function handleCliExecutionCompleted(payload) {
var statusIndicator = document.getElementById('cli-output-status-indicator');
var statusText = document.getElementById('cli-output-status-text');
if (statusIndicator) statusIndicator.className = 'status-indicator ' + (payload.success ? 'success' : 'error');
if (statusText) statusText.textContent = payload.success ? 'Completed in ' + formatDuration(payload.duration_ms) : 'Failed: ' + payload.status;
// Show toast notification
if (typeof addGlobalNotification === 'function') {
if (payload.success) {
addGlobalNotification('success', 'CLI execution completed', formatDuration(payload.duration_ms), 'CLI');
} else {
addGlobalNotification('error', 'CLI execution failed', payload.status, 'CLI');
}
}
currentCliExecution = null;
if (currentView === 'cli-manager') {
loadCliHistory().then(function() { renderCliHistory(); });
}
}
function handleCliExecutionError(payload) {
var statusIndicator = document.getElementById('cli-output-status-indicator');
var statusText = document.getElementById('cli-output-status-text');
if (statusIndicator) statusIndicator.className = 'status-indicator error';
if (statusText) statusText.textContent = 'Error: ' + payload.error;
// Show toast notification
if (typeof addGlobalNotification === 'function') {
addGlobalNotification('error', 'CLI execution error', payload.error, 'CLI');
}
currentCliExecution = null;
}
// ========== CLI Tool Install/Uninstall Wizards ==========
function openCliInstallWizard(toolName) {
var toolDescriptions = {
gemini: 'Google AI for code analysis and generation',
qwen: 'Alibaba AI assistant for coding',
codex: 'OpenAI code generation and understanding',
claude: 'Anthropic AI assistant'
};
var toolPackages = {
gemini: '@google/gemini-cli',
qwen: '@qwen-code/qwen-code',
codex: '@openai/codex',
claude: '@anthropic-ai/claude-code'
};
var modal = document.createElement('div');
modal.id = 'cliInstallModal';
modal.className = 'fixed inset-0 bg-black/50 flex items-center justify-center z-50';
modal.innerHTML =
'