import { tmpdir } from 'os'
import { mkdir, readFile, rm, writeFile } from 'fs/promises'
import { exec } from 'child_process'
import { existsSync } from 'fs'
import { promisify } from 'util'
import path from 'path'
import { exePath, homeDir } from '../utils/dirs'
import { managerLogger } from '../utils/logger'
const appName = 'mihomo-party'
function getTaskXml(asAdmin: boolean): string {
const runLevel = asAdmin ? 'HighestAvailable' : 'LeastPrivilege'
return `
true
PT3S
InteractiveToken
${runLevel}
Parallel
false
false
false
false
false
false
false
true
true
false
false
false
PT0S
3
"${exePath()}"
`
}
export async function checkAutoRun(): Promise {
if (process.platform === 'win32') {
const execPromise = promisify(exec)
// 先检查任务计划程序
try {
const { stdout } = await execPromise(
`chcp 437 && %SystemRoot%\\System32\\schtasks.exe /query /tn "${appName}"`
)
if (stdout.includes(appName)) {
return true
}
} catch {
// 任务计划程序中不存在,继续检查注册表
}
// 检查注册表备用方案
try {
const regPath = 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run'
const { stdout } = await execPromise(`reg query "${regPath}" /v "${appName}"`)
return stdout.includes(appName)
} catch {
return false
}
}
if (process.platform === 'darwin') {
const execPromise = promisify(exec)
const { stdout } = await execPromise(
`osascript -e 'tell application "System Events" to get the name of every login item'`
)
return stdout.includes(exePath().split('.app')[0].replace('/Applications/', ''))
}
if (process.platform === 'linux') {
return existsSync(path.join(homeDir, '.config', 'autostart', `${appName}.desktop`))
}
return false
}
export async function enableAutoRun(): Promise {
if (process.platform === 'win32') {
const execPromise = promisify(exec)
const taskFilePath = path.join(tmpdir(), `${appName}.xml`)
const { checkAdminPrivileges } = await import('../core/manager')
const isAdmin = await checkAdminPrivileges()
await writeFile(taskFilePath, Buffer.from(`\ufeff${getTaskXml(isAdmin)}`, 'utf-16le'))
let taskCreated = false
if (isAdmin) {
try {
await execPromise(
`%SystemRoot%\\System32\\schtasks.exe /create /tn "${appName}" /xml "${taskFilePath}" /f`
)
taskCreated = true
} catch (error) {
await managerLogger.warn('Failed to create scheduled task as admin:', error)
}
} else {
try {
await execPromise(
`powershell -NoProfile -Command "Start-Process schtasks -Verb RunAs -ArgumentList '/create', '/tn', '${appName}', '/xml', '${taskFilePath}', '/f' -WindowStyle Hidden -Wait"`
)
// 验证任务是否创建成功
await new Promise((resolve) => setTimeout(resolve, 1000))
const created = await checkAutoRun()
taskCreated = created
if (!created) {
await managerLogger.warn('Scheduled task creation may have failed or been rejected')
}
} catch {
await managerLogger.info('Scheduled task creation failed, trying registry fallback')
}
}
// 任务计划程序失败时使用注册表备用方案(适用于 Windows IoT LTSC 等受限环境)
if (!taskCreated) {
await managerLogger.info('Using registry fallback for auto-run')
try {
const regPath = 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run'
const regValue = `"${exePath()}"`
await execPromise(`reg add "${regPath}" /v "${appName}" /t REG_SZ /d ${regValue} /f`)
await managerLogger.info('Registry auto-run entry created successfully')
} catch (regError) {
await managerLogger.error('Failed to create registry auto-run entry:', regError)
}
}
}
if (process.platform === 'darwin') {
const execPromise = promisify(exec)
await execPromise(
`osascript -e 'tell application "System Events" to make login item at end with properties {path:"${exePath().split('.app')[0]}.app", hidden:false}'`
)
}
if (process.platform === 'linux') {
let desktop = `
[Desktop Entry]
Name=mihomo-party
Exec=${exePath()} %U
Terminal=false
Type=Application
Icon=mihomo-party
StartupWMClass=mihomo-party
Comment=Clash Party
Categories=Utility;
`
if (existsSync(`/usr/share/applications/${appName}.desktop`)) {
desktop = await readFile(`/usr/share/applications/${appName}.desktop`, 'utf8')
}
const autostartDir = path.join(homeDir, '.config', 'autostart')
if (!existsSync(autostartDir)) {
await mkdir(autostartDir, { recursive: true })
}
const desktopFilePath = path.join(autostartDir, `${appName}.desktop`)
await writeFile(desktopFilePath, desktop)
}
}
export async function disableAutoRun(): Promise {
if (process.platform === 'win32') {
const execPromise = promisify(exec)
const { checkAdminPrivileges } = await import('../core/manager')
const isAdmin = await checkAdminPrivileges()
// 删除任务计划程序中的任务
try {
if (isAdmin) {
await execPromise(`%SystemRoot%\\System32\\schtasks.exe /delete /tn "${appName}" /f`)
} else {
await execPromise(
`powershell -NoProfile -Command "Start-Process schtasks -Verb RunAs -ArgumentList '/delete', '/tn', '${appName}', '/f' -WindowStyle Hidden -Wait"`
)
}
} catch {
// 任务可能不存在,忽略错误
}
// 同时删除注册表备用方案
try {
const regPath = 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run'
await execPromise(`reg delete "${regPath}" /v "${appName}" /f`)
} catch {
// 注册表项可能不存在,忽略错误
}
}
if (process.platform === 'darwin') {
const execPromise = promisify(exec)
await execPromise(
`osascript -e 'tell application "System Events" to delete login item "${exePath().split('.app')[0].replace('/Applications/', '')}"'`
)
}
if (process.platform === 'linux') {
const desktopFilePath = path.join(homeDir, '.config', 'autostart', `${appName}.desktop`)
await rm(desktopFilePath)
}
}