/**
* 系统诊断页面
* 全面检测 ClawPanel 各项功能状态,快速定位问题
*/
import { api, getRequestLogs, clearRequestLogs } from '../lib/tauri-api.js'
import { wsClient } from '../lib/ws-client.js'
import { isOpenclawReady, isGatewayRunning } from '../lib/app-state.js'
import { isForeignGatewayError, showGatewayConflictGuidance } from '../lib/gateway-ownership.js'
import { icon, statusIcon } from '../lib/icons.js'
import { toast } from '../components/toast.js'
import { navigate } from '../router.js'
import { t } from '../lib/i18n.js'
export async function render() {
const page = document.createElement('div')
page.className = 'page'
page.innerHTML = `
`
// 总体状态概览
const allOk = info.appState.openclawReady && info.appState.gatewayRunning && info.wsClient.gatewayReady
html += `
${allOk ? `${statusIcon('ok')} ${t('chatDebug.systemOk')}` : `${statusIcon('warn')} ${t('chatDebug.issuesFound')}`}
${allOk ? t('chatDebug.allFunctionsOk') : t('chatDebug.someFunctionsError')}
`
// 应用状态
html += `
${t('chatDebug.sectionAppState')}
| ${t('chatDebug.openclawReady')} | ${info.appState.openclawReady ? statusIcon('ok') : statusIcon('err')} |
| ${t('chatDebug.gatewayRunning')} | ${info.appState.gatewayRunning ? statusIcon('ok') : statusIcon('err')} |
`
// WebSocket 状态
html += `
${t('chatDebug.sectionWs')}
| ${t('chatDebug.connStatus')} | ${info.wsClient.connected ? `${statusIcon('ok')} ${t('chatDebug.connected')}` : `${statusIcon('err')} ${t('chatDebug.notConnected')}`} |
| ${t('chatDebug.handshakeStatus')} | ${info.wsClient.gatewayReady ? `${statusIcon('ok')} ${t('chatDebug.completed')}` : `${statusIcon('err')} ${t('chatDebug.notCompleted')}`} |
| ${t('chatDebug.sessionKey')} | ${info.wsClient.sessionKey || t('chatDebug.empty')} |
`
// Node.js 环境
html += `
${t('chatDebug.sectionNode')}
`
if (info.nodeError) {
html += `
${statusIcon('err')} ${escapeHtml(info.nodeError)}
`
} else if (info.node) {
html += `
| ${t('chatDebug.installStatus')} | ${info.node.installed ? `${statusIcon('ok')} ${t('chatDebug.installed')}` : `${statusIcon('err')} ${t('chatDebug.notInstalled')}`} |
| ${t('chatDebug.version')} | ${info.node.version || t('chatDebug.unknownLabel')} |
`
}
html += `
`
// 版本信息
html += `
${t('chatDebug.sectionVersion')}
`
if (info.versionError) {
html += `
${statusIcon('err')} ${escapeHtml(info.versionError)}
`
} else if (info.version) {
html += `
| ${t('chatDebug.currentVersion')} | ${info.version.current || t('chatDebug.unknownLabel')} |
| ${t('chatDebug.recommendedVersion')} | ${info.version.recommended || t('chatDebug.notDetected')} |
| ${t('chatDebug.panelVersion')} | ${info.version.panel_version || t('chatDebug.unknownLabel')} |
| ${t('chatDebug.latestUpstream')} | ${info.version.latest || t('chatDebug.notDetected')} |
| ${t('chatDebug.deviationFromRecommended')} | ${info.version.ahead_of_recommended ? `${statusIcon('warn')} ${t('chatDebug.versionTooHigh')}` : info.version.is_recommended ? `${statusIcon('ok')} ${t('chatDebug.versionAligned')}` : `${statusIcon('warn')} ${t('chatDebug.versionNeedSwitch')}`} |
| ${t('chatDebug.latestAvailable')} | ${info.version.latest_update_available ? `${statusIcon('warn')} ${t('chatDebug.hasUpdate')}` : `${statusIcon('ok')} ${t('chatDebug.noUpdate')}`} |
`
}
html += `
`
// 配置文件
html += `
${t('chatDebug.sectionConfig')}
`
if (info.configError) {
html += `
${statusIcon('err')} ${escapeHtml(info.configError)}
`
} else if (info.config) {
const gw = info.config.gateway || {}
html += `
| gateway.port | ${gw.port || t('chatDebug.notSet')} |
| gateway.auth.token | ${gw.auth?.token ? `${statusIcon('ok')} ${t('chatDebug.set')}${typeof gw.auth.token === 'object' ? ' (SecretRef)' : ''}` : `${statusIcon('warn')} ${t('chatDebug.notSet')}`} |
| gateway.enabled | ${gw.enabled !== false ? statusIcon('ok') : statusIcon('err')} |
| gateway.mode | ${gw.mode || 'local'} |
`
}
html += `
`
// 服务状态
html += `
${t('chatDebug.sectionService')}
`
if (info.servicesError) {
html += `
${statusIcon('err')} ${escapeHtml(info.servicesError)}
`
} else if (info.services?.length > 0) {
const svc = info.services[0]
html += `
| ${t('chatDebug.cliInstall')} | ${svc.cli_installed !== false ? `${statusIcon('ok')} ${t('chatDebug.installed')}` : `${statusIcon('err')} ${t('chatDebug.notInstalled')}`} |
| ${t('chatDebug.runStatus')} | ${svc.running ? `${statusIcon('ok')} ${t('chatDebug.running')}` : `${statusIcon('err')} ${t('chatDebug.stopped')}`} |
| ${t('chatDebug.processPid')} | ${svc.pid || t('chatDebug.none')} |
| ${t('chatDebug.serviceLabel')} | ${svc.label || t('chatDebug.unknownLabel')} |
`
}
html += `
`
// 设备密钥
html += `
${t('chatDebug.sectionDevice')}
`
if (info.connectFrameError) {
html += `
${statusIcon('err')} ${escapeHtml(info.connectFrameError)}
`
} else if (info.connectFrame) {
const device = info.connectFrame.params?.device
html += `
${statusIcon('ok')} ${t('chatDebug.deviceKeySuccess')}
| ${t('chatDebug.deviceId')} | ${device?.id || t('chatDebug.none')} |
| ${t('chatDebug.publicKey')} | ${device?.publicKey ? device.publicKey.substring(0, 32) + '...' : t('chatDebug.none')} |
| ${t('chatDebug.signTime')} | ${device?.signedAt || t('chatDebug.none')} |
${t('chatDebug.viewConnectFrame')}
${escapeHtml(JSON.stringify(info.connectFrame, null, 2))}
`
}
html += `
`
// 诊断建议
html += `
${t('chatDebug.sectionDiagnosis')}
`
if (!info.node?.installed) {
html += `- ${statusIcon('err')} ${t('chatDebug.diagNodeNotInstalled')}
`
}
if (info.configError) {
html += `- ${statusIcon('err')} ${t('chatDebug.diagConfigMissing')}
`
}
if (info.servicesError || !info.services?.length || info.services[0]?.cli_installed === false) {
html += `- ${statusIcon('err')} ${t('chatDebug.diagCliNotInstalled')}
`
}
if (info.services?.length > 0 && !info.services[0]?.running) {
html += `- ${statusIcon('warn')} ${t('chatDebug.diagGatewayNotRunning')}
`
}
if (info.config && !info.config.gateway?.auth?.token) {
html += `- ${statusIcon('warn')} ${t('chatDebug.diagTokenNotSet')}
`
} else if (info.config && typeof info.config.gateway?.auth?.token === 'object') {
html += `- ${statusIcon('ok')} ${t('chatDebug.diagTokenSecretRef')}
`
}
if (info.connectFrameError) {
html += `- ${statusIcon('err')} ${t('chatDebug.diagDeviceKeyFailed')}
`
}
if (!info.wsClient.connected && info.services?.length > 0 && info.services[0]?.running) {
html += `- ${statusIcon('warn')} ${t('chatDebug.diagWsNotConnected', { port: info.config?.gateway?.port || 18789 })}
`
}
if (info.wsClient.connected && !info.wsClient.gatewayReady) {
html += `- ${statusIcon('warn')} ${t('chatDebug.diagWsHandshakeFailed')}
`
}
if (allOk) {
html += `- ${statusIcon('ok')} ${t('chatDebug.diagAllOk')}
`
}
html += `
`
html += `
${t('chatDebug.checkTime', { time: info.timestamp })}
`
html += `
`
el.innerHTML = html
}
// 配置诊断 / 自动修复(openclaw doctor)
async function handleDoctor(page, fix) {
const btnCheck = page.querySelector('#btn-doctor-check')
const btnFix = page.querySelector('#btn-doctor-fix')
const outputDiv = page.querySelector('#doctor-output')
const section = outputDiv?.querySelector('.config-section')
const pre = outputDiv?.querySelector('pre')
if (!outputDiv || !pre) return
// 清除之前的提示
section?.querySelectorAll('.doctor-tip').forEach(el => el.remove())
if (btnCheck) btnCheck.disabled = true
if (btnFix) btnFix.disabled = true
if (fix && btnFix) btnFix.textContent = t('chatDebug.fixing')
if (!fix && btnCheck) btnCheck.textContent = t('chatDebug.diagnosing')
outputDiv.style.display = 'block'
pre.textContent = fix ? t('chatDebug.runningDoctorFix') : t('chatDebug.runningDoctor')
pre.style.color = 'var(--text-secondary)'
try {
const result = fix ? await api.doctorFix() : await api.doctorCheck()
let text = result.output || ''
if (result.errors) text += '\n' + result.errors
const fullText = text.trim()
pre.textContent = fullText || (result.success ? t('chatDebug.noIssues') : t('chatDebug.diagDone'))
pre.style.color = result.success ? 'var(--success)' : 'var(--warning)'
if (fullText.includes('ERR_MODULE_NOT_FOUND') || fullText.includes('Cannot find module')) {
appendDoctorTip(section, t('chatDebug.installCorrupt'), t('chatDebug.installCorruptHint'))
toast(t('chatDebug.installCorruptToast'), 'warning')
} else if (fix && result.success) {
toast(t('chatDebug.configFixDone'), 'success')
} else if (fix) {
toast(t('chatDebug.configFixPartial'), 'warning')
}
} catch (e) {
const errMsg = e?.message || String(e)
pre.textContent = t('chatDebug.execFailed') + errMsg
pre.style.color = 'var(--error)'
if (errMsg.includes('ERR_MODULE_NOT_FOUND') || errMsg.includes('Cannot find module') || errMsg.includes('未找到')) {
appendDoctorTip(section, t('chatDebug.cliUnavailable'), t('chatDebug.cliUnavailableHint'))
}
toast(t('chatDebug.execFailed') + e, 'error')
} finally {
if (btnCheck) { btnCheck.disabled = false; btnCheck.textContent = t('chatDebug.btnDiagConfig') }
if (btnFix) { btnFix.disabled = false; btnFix.textContent = t('chatDebug.btnAutoFix') }
}
}
function appendDoctorTip(parent, title, body) {
if (!parent) return
const tip = document.createElement('div')
tip.className = 'doctor-tip'
tip.style.cssText = 'margin-top:var(--space-sm);padding:var(--space-sm);background:rgba(239,68,68,0.08);border-radius:var(--radius);font-size:var(--font-size-sm);color:var(--error);line-height:1.6'
tip.innerHTML = `