From 3b296bf54bf32e2dfabfaf748a630ea32c1ad08f Mon Sep 17 00:00:00 2001 From: MOMO0302-02 Date: Mon, 31 Aug 2026 02:04:14 -0700 Subject: [PATCH] fix: apply Smart core settings on hot reload and stop re-downloading the model * fix: apply Smart core settings on hot reload The Smart core switches (LightGBM, data collection, strategy, collector size) are delivered to the kernel through a generated global override script, but that script was only regenerated inside prepareCore(). The settings page persists the change and then hot reloads, so the reload re-applied the previous script and the new values silently did not reach the kernel until the core happened to be restarted. Regenerate the override before generating the profile on hot reload, and skip the write when the generated content is unchanged so routine hot reloads no longer churn the override file. * fix: reuse the downloaded Smart model when checking a profile The Smart core downloads Model.bin when it is missing from its working directory. Profile checks run the core with `-d `, which never receives the model, so every check starts a fresh download. That download runs after the previous core has already been stopped and the system proxy torn down, so it times out instead of completing, and a failed download leaves nothing behind - the next restart repeats it. On a restart here it cost 20s, against 0.5s once the model is in place. Copy the model over from the working directory before running the check, mirroring how the geo databases are already shared with the test dir. Co-Authored-By: Claude Opus 5 --------- Co-authored-by: Claude Opus 5 --- src/main/config/smartOverride.ts | 9 ++++++++- src/main/core/manager.ts | 2 ++ src/main/core/mihomoApi.ts | 5 ++++- src/main/core/smartModel.ts | 34 ++++++++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 src/main/core/smartModel.ts diff --git a/src/main/config/smartOverride.ts b/src/main/config/smartOverride.ts index 4a629c01..10daa373 100644 --- a/src/main/config/smartOverride.ts +++ b/src/main/config/smartOverride.ts @@ -1,6 +1,6 @@ import { overrideLogger } from '../utils/logger' import { getAppConfig } from './app' -import { addOverrideItem, removeOverrideItem, getOverrideItem } from './override' +import { addOverrideItem, removeOverrideItem, getOverrideItem, getOverride } from './override' const SMART_OVERRIDE_ID = 'smart-core-override' @@ -377,6 +377,13 @@ export async function createSmartOverride(): Promise { smartCollectorSize ) + // 热重载每次生效前都会重新生成覆写,内容未变时跳过写盘,避免无谓的时间戳刷新 + const existing = await getOverrideItem(SMART_OVERRIDE_ID) + if (existing) { + const current = await getOverride(SMART_OVERRIDE_ID, 'js') + if (current === template) return + } + await addOverrideItem({ id: SMART_OVERRIDE_ID, name: 'Smart Core Override', diff --git a/src/main/core/manager.ts b/src/main/core/manager.ts index 6c8a04c9..2be78f6d 100644 --- a/src/main/core/manager.ts +++ b/src/main/core/manager.ts @@ -46,6 +46,7 @@ import { getAxios } from './mihomoApi' import { generateProfile } from './factory' +import { syncSmartModelToTestDir } from './smartModel' import { checkAdminRestartForTun as checkAdminRestartForTunWithRestart, getSessionAdminStatus, @@ -967,6 +968,7 @@ export async function checkProfileConfig( ageSecretKey?: string ): Promise { const corePath = mihomoCorePath(core) + await syncSmartModelToTestDir() try { await execFilePromise(corePath, ['-t', '-f', configPath, '-d', mihomoTestDir()], { diff --git a/src/main/core/mihomoApi.ts b/src/main/core/mihomoApi.ts index 86f59a22..e382608f 100644 --- a/src/main/core/mihomoApi.ts +++ b/src/main/core/mihomoApi.ts @@ -2,7 +2,7 @@ import { createConnection } from 'net' import axios, { AxiosInstance } from 'axios' import WebSocket from 'ws' import { app } from 'electron' -import { getAppConfig, getControledMihomoConfig } from '../config' +import { getAppConfig, getControledMihomoConfig, manageSmartOverride } from '../config' import { mainWindow } from '../window' import { tray } from '../resolve/tray' import { calcTraffic } from '../utils/calc' @@ -434,6 +434,9 @@ export const mihomoHotReloadConfig = async (): Promise => { await restartCore() return } + // Smart 覆写脚本由应用配置生成,必须先同步再生成配置, + // 否则界面上改动的 Smart 选项会沿用旧脚本,要等到下次重启内核才生效 + await manageSmartOverride() const current = await generateProfile() const { diffWorkDir = false } = await getAppConfig() const configPath = diffWorkDir ? mihomoWorkConfigPath(current) : mihomoWorkConfigPath('work') diff --git a/src/main/core/smartModel.ts b/src/main/core/smartModel.ts new file mode 100644 index 00000000..8b3ec2c6 --- /dev/null +++ b/src/main/core/smartModel.ts @@ -0,0 +1,34 @@ +import { copyFile, stat } from 'fs/promises' +import { existsSync } from 'fs' +import path from 'path' +import { mihomoWorkDir, mihomoTestDir } from '../utils/dirs' +import { managerLogger } from '../utils/logger' + +const MODEL_FILE = 'Model.bin' + +async function isSourceNewer(sourcePath: string, targetPath: string): Promise { + try { + const [sourceStats, targetStats] = await Promise.all([stat(sourcePath), stat(targetPath)]) + return sourceStats.mtime > targetStats.mtime + } catch { + return true + } +} + +// Smart 内核在工作目录找不到 Model.bin 时会联网下载模型。而配置检查(mihomo -t)跑在独立的 +// test 目录里,该目录从不包含模型;这次检查又发生在旧内核已经停止、系统代理已经撤下之后, +// 于是下载必然超时失败,每次重启白白多等约 20 秒,且失败不留文件,下次重启重演一遍。 +// 正式工作目录已有模型时先同步过去,跳过这次注定失败的下载。 +export async function syncSmartModelToTestDir(): Promise { + const source = path.join(mihomoWorkDir(), MODEL_FILE) + if (!existsSync(source)) return + + const target = path.join(mihomoTestDir(), MODEL_FILE) + if (existsSync(target) && !(await isSourceNewer(source, target))) return + + try { + await copyFile(source, target) + } catch (error) { + managerLogger.warn('Failed to sync Model.bin into test dir', error) + } +}