mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-07-12 16:01:56 +08:00
feat: v0.9.1 — 面板设置页、网络代理、后台安装、模型服务商扩展、多项修复
新功能: - 新增独立面板设置页面(网络代理 + 代理测试 + 模型代理开关 + npm源) - 网络代理支持:下载类操作走代理,自动绕过内网地址 - 安装/升级/卸载改为后台执行,不再阻塞界面 - 全局任务状态栏:关闭弹窗后顶部显示进度,可重新查看日志 - 安装/卸载完成后自动刷新界面状态 - 新增多个模型服务商快捷配置(硅基流动、火山引擎、阿里云百炼、智谱AI、MiniMax、NVIDIA NIM、胜算云) - AI助手浮动按钮恢复,首次提示可拖动,实时聊天页隐藏 修复: - 修复版本更新误判(本地版本高于远端不再误弹更新) - 修复Windows下nvm/自定义Node路径CLI检测 - 修复npm EEXIST文件冲突(--force + 安装前自动清理) - 修复汉化版-zh.x后缀版本比较错误 - 修复模型URL自动拼接/v1问题 - 修复切换版本后Gateway重装失败(PATH缓存刷新) - 修复切换助手服务商时旧模型名残留 优化: - macOS图标改用docs/logo.png统一生成 - 内置推荐版本号更新到OpenClaw 2026.3.13 - 错误诊断增强(EEXIST识别) - 弹窗标题根据操作类型显示 - 新增版本维护文档
This commit is contained in:
@@ -48,13 +48,14 @@ async function runDetect(page) {
|
||||
<div class="stat-card loading-placeholder" style="height:48px;margin-top:8px"></div>
|
||||
`
|
||||
// 清除缓存,确保拿到最新检测结果
|
||||
invalidate('check_node', 'check_git', 'get_services_status', 'check_installation')
|
||||
invalidate('get_version_info', 'check_node', 'check_git', 'get_services_status', 'check_installation')
|
||||
// 并行检测 Node.js、Git、OpenClaw CLI、配置文件
|
||||
const [nodeRes, gitRes, clawRes, configRes] = await Promise.allSettled([
|
||||
const [nodeRes, gitRes, clawRes, configRes, versionRes] = await Promise.allSettled([
|
||||
api.checkNode(),
|
||||
api.checkGit(),
|
||||
api.getServicesStatus(),
|
||||
api.checkInstallation(),
|
||||
api.getVersionInfo(),
|
||||
])
|
||||
|
||||
const node = nodeRes.status === 'fulfilled' ? nodeRes.value : { installed: false }
|
||||
@@ -63,6 +64,7 @@ async function runDetect(page) {
|
||||
&& clawRes.value?.length > 0
|
||||
&& clawRes.value[0]?.cli_installed !== false
|
||||
let config = configRes.status === 'fulfilled' ? configRes.value : { installed: false }
|
||||
const version = versionRes.status === 'fulfilled' ? versionRes.value : null
|
||||
|
||||
// CLI 已装但配置缺失 → 自动创建默认配置
|
||||
if (cliOk && !config.installed) {
|
||||
@@ -81,7 +83,7 @@ async function runDetect(page) {
|
||||
api.configureGitHttps().catch(() => {})
|
||||
}
|
||||
|
||||
renderSteps(page, { node, git, cliOk, config })
|
||||
renderSteps(page, { node, git, cliOk, config, version })
|
||||
}
|
||||
|
||||
function stepIcon(ok) {
|
||||
@@ -89,7 +91,7 @@ function stepIcon(ok) {
|
||||
return `<span style="color:${color};font-weight:700;width:18px;display:inline-block">${ok ? '✓' : '✗'}</span>`
|
||||
}
|
||||
|
||||
function renderSteps(page, { node, git, cliOk, config }) {
|
||||
function renderSteps(page, { node, git, cliOk, config, version }) {
|
||||
const stepsEl = page.querySelector('#setup-steps')
|
||||
const nodeOk = node.installed
|
||||
const gitOk = git?.installed || false
|
||||
@@ -163,7 +165,12 @@ function renderSteps(page, { node, git, cliOk, config }) {
|
||||
${stepIcon(cliOk)} OpenClaw CLI
|
||||
</div>
|
||||
${cliOk
|
||||
? `<p style="color:var(--success);font-size:var(--font-size-sm)">CLI 可用</p>`
|
||||
? `<p style="color:var(--success);font-size:var(--font-size-sm)">CLI 可用</p>
|
||||
${version?.ahead_of_recommended && version?.recommended
|
||||
? `<div style="margin-top:8px;padding:8px 12px;background:var(--bg-tertiary);border-radius:var(--radius-sm);font-size:var(--font-size-xs);color:var(--warning,#f59e0b);line-height:1.6">
|
||||
检测到当前本地 OpenClaw ${version.current || ''} 高于当前面板推荐稳定版 ${version.recommended},可能存在兼容或稳定性风险。建议稍后到「关于」页回退到推荐版。
|
||||
</div>`
|
||||
: ''}`
|
||||
: renderInstallSection()
|
||||
}
|
||||
</div>
|
||||
@@ -297,7 +304,10 @@ function renderInstallSection() {
|
||||
|
||||
return `
|
||||
<p style="color:var(--text-secondary);font-size:var(--font-size-sm);margin-bottom:var(--space-sm)">
|
||||
选择版本后点击安装,将自动执行 npm 全局安装。
|
||||
点击安装后,将默认安装当前 ClawPanel 版本绑定的推荐稳定版;如需升降级,可稍后到「关于」页面切换版本。
|
||||
</p>
|
||||
<p style="color:var(--text-tertiary);font-size:var(--font-size-xs);line-height:1.6;margin:-4px 0 var(--space-sm)">
|
||||
如果你是为了体验最新版功能,建议先安装推荐稳定版再手动切换;若希望面板优先适配最新版,欢迎提交 issue。
|
||||
</p>
|
||||
<div style="display:flex;gap:var(--space-sm);margin-bottom:var(--space-sm)">
|
||||
<label class="setup-source-option" style="flex:1;cursor:pointer">
|
||||
@@ -499,90 +509,116 @@ function bindEvents(page, nodeOk, detectState) {
|
||||
installBtn.addEventListener('click', async () => {
|
||||
const source = page.querySelector('input[name="install-source"]:checked')?.value || 'chinese'
|
||||
const registry = page.querySelector('#registry-select')?.value
|
||||
const modal = showUpgradeModal()
|
||||
const modal = showUpgradeModal('安装 OpenClaw')
|
||||
let unlistenLog, unlistenProgress
|
||||
|
||||
setUpgrading(true)
|
||||
try {
|
||||
if (window.__TAURI_INTERNALS__) {
|
||||
try {
|
||||
const { listen } = await import('@tauri-apps/api/event')
|
||||
unlistenLog = await listen('upgrade-log', (e) => modal.appendLog(e.payload))
|
||||
unlistenProgress = await listen('upgrade-progress', (e) => modal.setProgress(e.payload))
|
||||
} catch { /* Web 模式无 Tauri event */ }
|
||||
} else {
|
||||
modal.appendLog('Web 模式:安装日志不可用,请等待完成...')
|
||||
}
|
||||
|
||||
// 先设置镜像源
|
||||
if (registry) {
|
||||
modal.appendLog(`设置 npm 镜像源: ${registry}`)
|
||||
try { await api.setNpmRegistry(registry) } catch {}
|
||||
}
|
||||
|
||||
const msg = await api.upgradeOpenclaw(source)
|
||||
modal.setDone(msg)
|
||||
|
||||
// 安装成功后自动安装 Gateway
|
||||
modal.appendLog('正在安装 Gateway 服务...')
|
||||
try {
|
||||
await api.installGateway()
|
||||
modal.appendHtmlLog(`${statusIcon('ok', 14)} Gateway 服务已安装`)
|
||||
} catch (e) {
|
||||
modal.appendHtmlLog(`${statusIcon('warn', 14)} Gateway 安装失败: ${e}`)
|
||||
}
|
||||
|
||||
// 确保 openclaw.json 有关键默认值,否则 Gateway 启动不了或功能受限
|
||||
try {
|
||||
const config = await api.readOpenclawConfig()
|
||||
if (config) {
|
||||
let patched = false
|
||||
if (!config.gateway) config.gateway = {}
|
||||
if (!config.gateway.mode) {
|
||||
config.gateway.mode = 'local'
|
||||
patched = true
|
||||
modal.appendHtmlLog(`${statusIcon('ok', 14)} 已设置 Gateway 运行模式为 local`)
|
||||
}
|
||||
if (!config.tools || config.tools.profile !== 'full') {
|
||||
config.tools = { profile: 'full', sessions: { visibility: 'all' }, ...(config.tools || {}) }
|
||||
config.tools.profile = 'full'
|
||||
if (!config.tools.sessions) config.tools.sessions = {}
|
||||
config.tools.sessions.visibility = 'all'
|
||||
patched = true
|
||||
modal.appendHtmlLog(`${statusIcon('ok', 14)} 已开启 Agent 工具全部权限`)
|
||||
}
|
||||
if (patched) await api.writeOpenclawConfig(config)
|
||||
}
|
||||
} catch (e) {
|
||||
modal.appendHtmlLog(`${statusIcon('warn', 14)} 自动配置失败: ${e}`)
|
||||
}
|
||||
|
||||
toast('OpenClaw 安装成功', 'success')
|
||||
setTimeout(() => window.location.reload(), 1500)
|
||||
} catch (e) {
|
||||
const errStr = String(e)
|
||||
modal.appendLog(errStr)
|
||||
// 等待 Tauri 事件队列中残留的 npm 日志行被 JS 处理完毕,
|
||||
// 确保 getLogText() 包含完整输出(含 exit code / ENOENT 等关键行)
|
||||
await new Promise(r => setTimeout(r, 150))
|
||||
const fullLog = modal.getLogText() + '\n' + errStr
|
||||
const diagnosis = diagnoseInstallError(fullLog)
|
||||
modal.setError(diagnosis.title)
|
||||
if (diagnosis.hint) modal.appendLog('')
|
||||
if (diagnosis.hint) modal.appendHtmlLog(`${statusIcon('info', 14)} ${diagnosis.hint}`)
|
||||
if (diagnosis.command) modal.appendHtmlLog(`${icon('clipboard', 14)} ${diagnosis.command}`)
|
||||
if (window.__openAIDrawerWithError) {
|
||||
window.__openAIDrawerWithError({
|
||||
title: diagnosis.title,
|
||||
error: fullLog,
|
||||
scene: '初始安装 OpenClaw',
|
||||
hint: diagnosis.hint,
|
||||
})
|
||||
}
|
||||
} finally {
|
||||
const cleanup = () => {
|
||||
setUpgrading(false)
|
||||
unlistenLog?.()
|
||||
unlistenProgress?.()
|
||||
unlistenDone?.()
|
||||
unlistenError?.()
|
||||
}
|
||||
|
||||
let unlistenDone, unlistenError
|
||||
|
||||
try {
|
||||
if (window.__TAURI_INTERNALS__) {
|
||||
const { listen } = await import('@tauri-apps/api/event')
|
||||
unlistenLog = await listen('upgrade-log', (e) => modal.appendLog(e.payload))
|
||||
unlistenProgress = await listen('upgrade-progress', (e) => modal.setProgress(e.payload))
|
||||
|
||||
// 后台任务完成:继续安装 Gateway + 自动配置
|
||||
unlistenDone = await listen('upgrade-done', async (e) => {
|
||||
cleanup()
|
||||
modal.setDone(typeof e.payload === 'string' ? e.payload : '安装完成')
|
||||
|
||||
// 安装成功后自动安装 Gateway
|
||||
modal.appendLog('正在安装 Gateway 服务...')
|
||||
try {
|
||||
await api.installGateway()
|
||||
modal.appendHtmlLog(`${statusIcon('ok', 14)} Gateway 服务已安装`)
|
||||
} catch (ge) {
|
||||
modal.appendHtmlLog(`${statusIcon('warn', 14)} Gateway 安装失败: ${ge}`)
|
||||
}
|
||||
|
||||
// 确保 openclaw.json 有关键默认值
|
||||
try {
|
||||
const config = await api.readOpenclawConfig()
|
||||
if (config) {
|
||||
let patched = false
|
||||
if (!config.gateway) config.gateway = {}
|
||||
if (!config.gateway.mode) {
|
||||
config.gateway.mode = 'local'
|
||||
patched = true
|
||||
modal.appendHtmlLog(`${statusIcon('ok', 14)} 已设置 Gateway 运行模式为 local`)
|
||||
}
|
||||
if (!config.tools || config.tools.profile !== 'full') {
|
||||
config.tools = { profile: 'full', sessions: { visibility: 'all' }, ...(config.tools || {}) }
|
||||
config.tools.profile = 'full'
|
||||
if (!config.tools.sessions) config.tools.sessions = {}
|
||||
config.tools.sessions.visibility = 'all'
|
||||
patched = true
|
||||
modal.appendHtmlLog(`${statusIcon('ok', 14)} 已开启 Agent 工具全部权限`)
|
||||
}
|
||||
if (patched) await api.writeOpenclawConfig(config)
|
||||
}
|
||||
} catch (ce) {
|
||||
modal.appendHtmlLog(`${statusIcon('warn', 14)} 自动配置失败: ${ce}`)
|
||||
}
|
||||
|
||||
toast('OpenClaw 安装成功', 'success')
|
||||
setTimeout(() => window.location.reload(), 1500)
|
||||
})
|
||||
|
||||
// 后台任务失败
|
||||
unlistenError = await listen('upgrade-error', async (e) => {
|
||||
cleanup()
|
||||
const errStr = String(e.payload || '未知错误')
|
||||
modal.appendLog(errStr)
|
||||
await new Promise(r => setTimeout(r, 150))
|
||||
const fullLog = modal.getLogText() + '\n' + errStr
|
||||
const diagnosis = diagnoseInstallError(fullLog)
|
||||
modal.setError(diagnosis.title)
|
||||
if (diagnosis.hint) modal.appendLog('')
|
||||
if (diagnosis.hint) modal.appendHtmlLog(`${statusIcon('info', 14)} ${diagnosis.hint}`)
|
||||
if (diagnosis.command) modal.appendHtmlLog(`${icon('clipboard', 14)} ${diagnosis.command}`)
|
||||
if (window.__openAIDrawerWithError) {
|
||||
window.__openAIDrawerWithError({ title: diagnosis.title, error: fullLog, scene: '初始安装 OpenClaw', hint: diagnosis.hint })
|
||||
}
|
||||
})
|
||||
|
||||
// 先设置镜像源
|
||||
if (registry) {
|
||||
modal.appendLog(`设置 npm 镜像源: ${registry}`)
|
||||
try { await api.setNpmRegistry(registry) } catch {}
|
||||
}
|
||||
|
||||
// 发起后台任务(立即返回)
|
||||
await api.upgradeOpenclaw(source)
|
||||
modal.appendLog('后台安装任务已启动,请等待完成...')
|
||||
} else {
|
||||
// Web 模式:同步等待
|
||||
modal.appendLog('Web 模式:安装日志不可用,请等待完成...')
|
||||
if (registry) {
|
||||
modal.appendLog(`设置 npm 镜像源: ${registry}`)
|
||||
try { await api.setNpmRegistry(registry) } catch {}
|
||||
}
|
||||
const msg = await api.upgradeOpenclaw(source)
|
||||
modal.setDone(msg)
|
||||
toast('OpenClaw 安装成功', 'success')
|
||||
setTimeout(() => window.location.reload(), 1500)
|
||||
cleanup()
|
||||
}
|
||||
} catch (e) {
|
||||
cleanup()
|
||||
const errStr = String(e)
|
||||
modal.appendLog(errStr)
|
||||
const fullLog = modal.getLogText() + '\n' + errStr
|
||||
const diagnosis = diagnoseInstallError(fullLog)
|
||||
modal.setError(diagnosis.title)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user