mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-07-04 22:21:26 +08:00
fix(openclaw): stabilize windows install and pairing
This commit is contained in:
@@ -49,6 +49,20 @@ function readBoundCliPath(panelConfig) {
|
||||
return String(panelConfig?.openclawCliPath || '').trim()
|
||||
}
|
||||
|
||||
function quoteCommandPath(path) {
|
||||
return String(path || '').replace(/"/g, '\\"')
|
||||
}
|
||||
|
||||
function openclawInstallDirForCommand(path) {
|
||||
const value = String(path || '').trim()
|
||||
if (!value) return ''
|
||||
const normalized = value.replace(/\\/g, '/')
|
||||
if (/\/openclaw(?:\.cmd|\.exe|\.bat|\.ps1|\.js)?$/i.test(normalized)) {
|
||||
return value.replace(/[\\/][^\\/]+$/, '')
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
let _foreignGatewayPromptKey = ''
|
||||
|
||||
export function isForeignGatewayService(service) {
|
||||
@@ -236,14 +250,15 @@ export async function showGatewayConflictGuidance({ error = null, service = null
|
||||
|
||||
/** 根据安装来源返回卸载命令 */
|
||||
function uninstallCommandForSource(source, path) {
|
||||
const isWin = navigator.platform?.startsWith('Win') || navigator.userAgent?.includes('Windows')
|
||||
if (source === 'standalone') {
|
||||
const isWin = navigator.platform?.startsWith('Win') || navigator.userAgent?.includes('Windows')
|
||||
const p = escapeHtml(path || '')
|
||||
const p = quoteCommandPath(openclawInstallDirForCommand(path))
|
||||
return isWin ? `rmdir /s /q "${p}"` : `rm -rf "${p}"`
|
||||
}
|
||||
if (source === 'npm-official' || source === 'official') return 'npm uninstall -g openclaw'
|
||||
// npm-zh, npm-global, and others
|
||||
return 'npm uninstall -g @qingchencloud/openclaw-zh'
|
||||
if (source === 'npm-zh' || source === 'npm-global') return 'npm uninstall -g @qingchencloud/openclaw-zh'
|
||||
const p = quoteCommandPath(path)
|
||||
return isWin ? `del /f /q "${p}"` : `rm -f "${p}"`
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -259,6 +274,11 @@ export async function showInstallationCleanup({ onRefresh = null } = {}) {
|
||||
const installations = dedupeOpenclawInstallations(Array.isArray(versionInfo?.all_installations) ? versionInfo.all_installations : [])
|
||||
const boundPath = readBoundCliPath(panelConfig)
|
||||
const currentPath = versionInfo?.cli_path || ''
|
||||
const hasActiveBoundInstall = installations.some(inst =>
|
||||
inst?.active
|
||||
&& boundPath
|
||||
&& openclawInstallationIdentity({ path: inst.path }) === openclawInstallationIdentity({ path: boundPath })
|
||||
)
|
||||
|
||||
const sourceLabel = (src) => cliSourceLabel(src)
|
||||
|
||||
@@ -277,16 +297,21 @@ export async function showInstallationCleanup({ onRefresh = null } = {}) {
|
||||
|
||||
const uninstallCmd = uninstallCommandForSource(inst.source, inst.path)
|
||||
|
||||
// 操作区:非活跃的安装显示卸载命令 + 复制按钮;活跃的显示绑定按钮
|
||||
// 操作区:所有未绑定安装都可以绑定;非活跃安装额外显示清理命令。
|
||||
let actions = ''
|
||||
if (isActive && !isBound) {
|
||||
actions = `<button class="btn btn-primary btn-xs cleanup-bind-btn" data-path="${escapeHtml(inst.path)}" style="margin-top:8px">${t('services.cleanupBindThis')}</button>`
|
||||
} else if (!isActive) {
|
||||
const shouldOfferBind = !isBound && (!hasActiveBoundInstall || isActive)
|
||||
const bindButton = shouldOfferBind
|
||||
? `<button class="btn btn-primary btn-xs cleanup-bind-btn" data-path="${escapeHtml(inst.path)}">${t('services.cleanupBindThis')}</button>`
|
||||
: ''
|
||||
if (!isActive) {
|
||||
actions = `
|
||||
<div style="margin-top:8px;display:flex;gap:6px;align-items:center;flex-wrap:wrap">
|
||||
${bindButton}
|
||||
<code style="flex:1;min-width:0;font-size:11px;padding:4px 8px;background:var(--bg-tertiary);border-radius:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;user-select:all" title="${escapeHtml(uninstallCmd)}">${escapeHtml(uninstallCmd)}</code>
|
||||
<button class="btn btn-secondary btn-xs cleanup-copy-cmd" data-cmd="${escapeHtml(uninstallCmd)}" style="flex-shrink:0">${t('services.cleanupCopyCmd')}</button>
|
||||
</div>`
|
||||
} else if (bindButton) {
|
||||
actions = `<div style="margin-top:8px">${bindButton}</div>`
|
||||
}
|
||||
|
||||
return `
|
||||
|
||||
@@ -590,17 +590,10 @@ export class WsClient {
|
||||
const result = await api.autoPairDevice()
|
||||
console.log('[ws] 配对结果:', result)
|
||||
|
||||
// 配对后桌面端需要 reload Gateway 使 allowedOrigins 生效;Web/headless 不能隐式重载反代后的服务。
|
||||
if (isTauriRuntime()) {
|
||||
try {
|
||||
await api.reloadGateway()
|
||||
console.log('[ws] Gateway 已重载')
|
||||
} catch (e) {
|
||||
console.warn('[ws] reloadGateway 失败(非致命):', e)
|
||||
}
|
||||
} else {
|
||||
console.log('[ws] Web/headless 模式跳过自动 reload Gateway')
|
||||
}
|
||||
// 这里只修配对文件,不自动重启 Gateway。
|
||||
// Windows 上手动启动的 Gateway 会被 restart/stop 打断,表现为“启动后一会就停止”。
|
||||
// Gateway 对设备配对文件按连接读取;如遇 origin 配置变更,交由用户手动重启。
|
||||
console.log('[ws] 自动配对文件已修复,跳过自动重启 Gateway')
|
||||
|
||||
// 修复 #160: 不调用 reconnect()(它会重置 _autoPairAttempts 导致无限循环),
|
||||
// 而是直接重连一次。如果仍然失败,_autoPairAttempts 不会被重置,不会再次触发自动修复。
|
||||
|
||||
Reference in New Issue
Block a user