fix(core): upgrade mihomo atomically instead of letting it self-update

mihomo's updater truncates its own running executable in place; macOS kills
that under Hardened Runtime and leaves a 0-byte core. Verge now downloads the
release, verifies a staged copy next to the target, and renames it into place,
which never touches the inode the running core executes from.

Closes #6834
This commit is contained in:
wonfen
2026-08-21 14:41:57 +08:00
parent e71cf0ec68
commit 1b75ea34f9
9 changed files with 529 additions and 12 deletions
@@ -15,12 +15,12 @@ import { useLockFn } from 'ahooks'
import type { Ref } from 'react'
import { useImperativeHandle, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { closeAllConnections, upgradeCore } from 'tauri-plugin-mihomo-api'
import { closeAllConnections } from 'tauri-plugin-mihomo-api'
import { BaseDialog, DialogRef } from '@/components/base'
import { useClash, useClashInfo } from '@/hooks/use-clash'
import { useVerge } from '@/hooks/use-verge'
import { changeClashCore, restartCore } from '@/services/cmds'
import { changeClashCore, restartCore, upgradeClashCore } from '@/services/cmds'
import { showNotice } from '@/services/notice-service'
const VALID_CORE = [
@@ -97,19 +97,16 @@ export function ClashCoreViewer({ ref }: { ref?: Ref<DialogRef> }) {
const onUpgrade = useLockFn(async () => {
try {
setUpgrading(true)
await upgradeCore()
setUpgrading(false)
mutateVersion()
const report = await upgradeClashCore()
showNotice.success(
t('settings.feedback.notifications.clash.versionUpdated'),
report.upgraded
? t('settings.feedback.notifications.clash.versionUpdated')
: t('settings.feedback.notifications.clash.alreadyLatestVersion'),
)
} catch (err: any) {
} catch (err) {
showNotice.error(err)
} finally {
setUpgrading(false)
const errMsg = err?.response?.data?.message ?? String(err)
const showMsg = errMsg.includes('already using latest version')
? t('settings.feedback.notifications.clash.alreadyLatestVersion')
: errMsg
showNotice.info(showMsg)
}
})