use unix socket and namedpipe instead of http api

This commit is contained in:
pompurin404 2024-09-27 16:26:54 +08:00
parent c516f8d1d9
commit c420f0397c
No known key found for this signature in database
6 changed files with 190 additions and 211 deletions

View File

@ -1,7 +1,6 @@
import { controledMihomoConfigPath } from '../utils/dirs' import { controledMihomoConfigPath } from '../utils/dirs'
import { readFile, writeFile } from 'fs/promises' import { readFile, writeFile } from 'fs/promises'
import yaml from 'yaml' import yaml from 'yaml'
import { getAxios } from '../core/mihomoApi'
import { generateProfile } from '../core/factory' import { generateProfile } from '../core/factory'
import { getAppConfig } from './app' import { getAppConfig } from './app'
import { defaultControledMihomoConfig } from '../utils/template' import { defaultControledMihomoConfig } from '../utils/template'
@ -52,9 +51,6 @@ export async function patchControledMihomoConfig(patch: Partial<IMihomoConfig>):
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
delete controledMihomoConfig?.tun?.device delete controledMihomoConfig?.tun?.device
} }
if (patch['external-controller'] || patch.secret) {
await getAxios(true)
}
await generateProfile() await generateProfile()
await writeFile(controledMihomoConfigPath(), yaml.stringify(controledMihomoConfig), 'utf-8') await writeFile(controledMihomoConfigPath(), yaml.stringify(controledMihomoConfig), 'utf-8')
} }

View File

@ -113,25 +113,19 @@ export async function startCore(detached = false): Promise<Promise<void>[]> {
}) })
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
child.stdout?.on('data', async (data) => { child.stdout?.on('data', async (data) => {
if (data.toString().includes('configure tun interface: operation not permitted')) { const str = data.toString()
if (str.includes('configure tun interface: operation not permitted')) {
patchControledMihomoConfig({ tun: { enable: false } }) patchControledMihomoConfig({ tun: { enable: false } })
mainWindow?.webContents.send('controledMihomoConfigUpdated') mainWindow?.webContents.send('controledMihomoConfigUpdated')
ipcMain.emit('updateTrayMenu') ipcMain.emit('updateTrayMenu')
reject('虚拟网卡启动失败, 请尝试手动授予内核权限') reject('虚拟网卡启动失败, 请尝试手动授予内核权限')
} }
if (data.toString().includes('External controller listen error')) {
if (retry) { if (
retry-- (process.platform !== 'win32' && str.includes('RESTful API unix listening at')) ||
try { (process.platform === 'win32' && str.includes('RESTful API pipe listening at'))
resolve(await startCore()) ) {
} catch (e) { await autoGrantUnixSocket()
reject(e)
}
} else {
reject('内核连接失败, 请尝试修改外部控制端口或重启电脑')
}
}
if (data.toString().includes('RESTful API listening at')) {
resolve([ resolve([
new Promise((resolve) => { new Promise((resolve) => {
child.stdout?.on('data', async (data) => { child.stdout?.on('data', async (data) => {
@ -223,6 +217,25 @@ async function checkProfile(): Promise<void> {
} }
} }
async function autoGrantUnixSocket(): Promise<void> {
if (process.platform === 'win32') return
const { encryptedPassword } = await getAppConfig()
const { 'external-controller-unix': mihomoUnix = 'mihomo-party.sock' } =
await getControledMihomoConfig()
const execPromise = promisify(exec)
if (encryptedPassword && isEncryptionAvailable()) {
try {
const password = safeStorage.decryptString(Buffer.from(encryptedPassword))
await execPromise(
`echo "${password}" | sudo -S chmod 777 "${path.join(mihomoWorkDir(), mihomoUnix)}"`
)
} catch (error) {
patchAppConfig({ encryptedPassword: undefined })
throw error
}
}
}
export async function autoGrantCorePermition(corePath: string): Promise<void> { export async function autoGrantCorePermition(corePath: string): Promise<void> {
if (process.platform === 'win32') return if (process.platform === 'win32') return
const { encryptedPassword } = await getAppConfig() const { encryptedPassword } = await getAppConfig()

View File

@ -1,77 +1,104 @@
import axios, { AxiosInstance } from 'axios' import net from 'net'
import { getRuntimeConfig } from './factory'
import { getAppConfig, getControledMihomoConfig } from '../config' import { getAppConfig, getControledMihomoConfig } from '../config'
import { mainWindow } from '..' import { mainWindow } from '..'
import WebSocket from 'ws'
import { tray } from '../resolve/tray' import { tray } from '../resolve/tray'
import { calcTraffic } from '../utils/calc' import { calcTraffic } from '../utils/calc'
import { getRuntimeConfig } from './factory' import { join } from 'path'
import { mihomoWorkDir } from '../utils/dirs'
let axiosIns: AxiosInstance = null! type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
let mihomoTrafficWs: WebSocket | null = null
let mihomoTrafficWs: net.Socket | null = null
let trafficRetry = 10 let trafficRetry = 10
let mihomoMemoryWs: WebSocket | null = null let mihomoMemoryWs: net.Socket | null = null
let memoryRetry = 10 let memoryRetry = 10
let mihomoLogsWs: WebSocket | null = null let mihomoLogsWs: net.Socket | null = null
let logsRetry = 10 let logsRetry = 10
let mihomoConnectionsWs: WebSocket | null = null let mihomoConnectionsWs: net.Socket | null = null
let connectionsRetry = 10 let connectionsRetry = 10
export const getAxios = async (force: boolean = false): Promise<AxiosInstance> => { function trimJson(data: string): string {
if (axiosIns && !force) return axiosIns if (data.trim().length === 0) return ''
const controledMihomoConfig = await getControledMihomoConfig() const start = data.indexOf('{')
let server = controledMihomoConfig['external-controller'] const end = data.lastIndexOf('}')
const secret = controledMihomoConfig.secret ?? '' return data.slice(start, end + 1)
if (server?.startsWith(':')) server = `127.0.0.1${server}`
axiosIns = axios.create({
baseURL: `http://${server}`,
proxy: false,
headers: secret ? { Authorization: `Bearer ${secret}` } : {},
timeout: 15000
})
axiosIns.interceptors.response.use(
(response) => {
return response.data
},
(error) => {
if (error.response && error.response.data) {
return Promise.reject(error.response.data)
}
return Promise.reject(error)
}
)
return axiosIns
} }
export async function mihomoVersion(): Promise<IMihomoVersion> { async function mihomoHttp<T>(method: HttpMethod, path: string, data?: object): Promise<T> {
const instance = await getAxios() const {
return await instance.get('/version') 'external-controller-pipe': mihomoPipe = '\\\\.\\pipe\\MihomoParty\\mihomo',
'external-controller-unix': mihomoUnix = 'mihomo-party.sock'
} = await getControledMihomoConfig()
return new Promise((resolve, reject) => {
const client = net.connect(
process.platform === 'win32' ? mihomoPipe : join(mihomoWorkDir(), mihomoUnix)
)
client.on('data', function (res) {
try {
const data = trimJson(res.toString().split('\r\n\r\n')[1])
if (res.toString().includes('HTTP/1.1 4') || res.toString().includes('HTTP/1.1 5')) {
reject(data ? JSON.parse(data) : undefined)
} else {
resolve(data ? JSON.parse(data) : undefined)
}
} catch (e) {
reject(e)
} finally {
client.end()
}
})
client.on('error', function (error) {
reject(error)
})
if (data) {
const json = JSON.stringify(data)
client.write(
`${method} ${path} HTTP/1.1\r\nHost: mihomo-party\r\nContent-Type: application/json\r\nContent-Length: ${json.length}\r\n\r\n${json}`
)
} else {
client.write(`${method} ${path} HTTP/1.1\r\nHost: mihomo-party\r\n\r\n`)
}
})
}
async function mihomoWs(path: string): Promise<net.Socket> {
const {
'external-controller-pipe': mihomoPipe = '\\\\.\\pipe\\MihomoParty\\mihomo',
'external-controller-unix': mihomoUnix = 'mihomo-party.sock'
} = await getControledMihomoConfig()
const client = net.connect(
process.platform === 'win32' ? mihomoPipe : join(mihomoWorkDir(), mihomoUnix)
)
client.write(
`GET ${path} HTTP/1.1\r\nHost: mihomo-party\r\nConnection: Upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: xxxxxxxxxxxxxxxxxxxxxxxx\r\n\r\n`
)
return client
}
export const mihomoVersion = async (): Promise<IMihomoVersion> => {
return await mihomoHttp('GET', '/version')
} }
export const patchMihomoConfig = async (patch: Partial<IMihomoConfig>): Promise<void> => { export const patchMihomoConfig = async (patch: Partial<IMihomoConfig>): Promise<void> => {
const instance = await getAxios() return await mihomoHttp('PATCH', '/configs', patch)
return await instance.patch('/configs', patch)
} }
export const mihomoCloseConnection = async (id: string): Promise<void> => { export const mihomoCloseConnection = async (id: string): Promise<void> => {
const instance = await getAxios() return await mihomoHttp('DELETE', `/connection/${id}`)
return await instance.delete(`/connections/${encodeURIComponent(id)}`)
} }
export const mihomoCloseAllConnections = async (): Promise<void> => { export const mihomoCloseAllConnections = async (): Promise<void> => {
const instance = await getAxios() return await mihomoHttp('DELETE', '/connections')
return await instance.delete('/connections')
} }
export const mihomoRules = async (): Promise<IMihomoRulesInfo> => { export const mihomoRules = async (): Promise<IMihomoRulesInfo> => {
const instance = await getAxios() return await mihomoHttp('GET', '/rules')
return await instance.get('/rules')
} }
export const mihomoProxies = async (): Promise<IMihomoProxies> => { export const mihomoProxies = async (): Promise<IMihomoProxies> => {
const instance = await getAxios() const proxies = (await mihomoHttp('GET', '/proxies')) as IMihomoProxies
const proxies = (await instance.get('/proxies')) as IMihomoProxies
if (!proxies.proxies['GLOBAL']) { if (!proxies.proxies['GLOBAL']) {
throw new Error('GLOBAL proxy not found') throw new Error('GLOBAL proxy not found')
} }
@ -102,265 +129,194 @@ export const mihomoGroups = async (): Promise<IMihomoMixedGroup[]> => {
} }
export const mihomoProxyProviders = async (): Promise<IMihomoProxyProviders> => { export const mihomoProxyProviders = async (): Promise<IMihomoProxyProviders> => {
const instance = await getAxios() return await mihomoHttp('GET', '/providers/proxies')
return await instance.get('/providers/proxies')
} }
export const mihomoUpdateProxyProviders = async (name: string): Promise<void> => { export const mihomoUpdateProxyProviders = async (name: string): Promise<void> => {
const instance = await getAxios() return await mihomoHttp('PUT', `/providers/proxies/${encodeURIComponent(name)}`)
return await instance.put(`/providers/proxies/${encodeURIComponent(name)}`)
} }
export const mihomoRuleProviders = async (): Promise<IMihomoRuleProviders> => { export const mihomoRuleProviders = async (): Promise<IMihomoRuleProviders> => {
const instance = await getAxios() return await mihomoHttp('GET', '/providers/rules')
return await instance.get('/providers/rules')
} }
export const mihomoUpdateRuleProviders = async (name: string): Promise<void> => { export const mihomoUpdateRuleProviders = async (name: string): Promise<void> => {
const instance = await getAxios() return await mihomoHttp('PUT', `/providers/rules/${encodeURIComponent(name)}`)
return await instance.put(`/providers/rules/${encodeURIComponent(name)}`)
} }
export const mihomoChangeProxy = async (group: string, proxy: string): Promise<IMihomoProxy> => { export const mihomoChangeProxy = async (group: string, proxy: string): Promise<IMihomoProxy> => {
const instance = await getAxios() return await mihomoHttp('PUT', `/proxies/${encodeURIComponent(group)}`, { name: proxy })
return await instance.put(`/proxies/${encodeURIComponent(group)}`, { name: proxy })
} }
export const mihomoUpgradeGeo = async (): Promise<void> => { export const mihomoUpgradeGeo = async (): Promise<void> => {
const instance = await getAxios() return await mihomoHttp('POST', '/configs/geo')
return await instance.post('/configs/geo')
} }
export const mihomoProxyDelay = async (proxy: string, url?: string): Promise<IMihomoDelay> => { export const mihomoProxyDelay = async (proxy: string, url?: string): Promise<IMihomoDelay> => {
const appConfig = await getAppConfig() const appConfig = await getAppConfig()
const { delayTestUrl, delayTestTimeout } = appConfig const { delayTestUrl, delayTestTimeout } = appConfig
const instance = await getAxios()
return await instance.get(`/proxies/${encodeURIComponent(proxy)}/delay`, { return await mihomoHttp(
params: { 'GET',
url: url || delayTestUrl || 'https://www.gstatic.com/generate_204', `/proxies/${encodeURIComponent(proxy)}/delay?url=${encodeURIComponent(url || delayTestUrl || 'https://www.gstatic.com/generate_204')}&timeout=${delayTestTimeout || 5000}`
timeout: delayTestTimeout || 5000 )
}
})
} }
export const mihomoGroupDelay = async (group: string, url?: string): Promise<IMihomoGroupDelay> => { export const mihomoGroupDelay = async (group: string, url?: string): Promise<IMihomoGroupDelay> => {
const appConfig = await getAppConfig() const appConfig = await getAppConfig()
const { delayTestUrl, delayTestTimeout } = appConfig const { delayTestUrl, delayTestTimeout } = appConfig
const instance = await getAxios() return await mihomoHttp(
return await instance.get(`/group/${encodeURIComponent(group)}/delay`, { 'GET',
params: { `/proxies/${encodeURIComponent(group)}/delay?url=${encodeURIComponent(url || delayTestUrl || 'https://www.gstatic.com/generate_204')}&timeout=${delayTestTimeout || 5000}`
url: url || delayTestUrl || 'https://www.gstatic.com/generate_204', )
timeout: delayTestTimeout || 5000
}
})
} }
export const mihomoUpgrade = async (): Promise<void> => { export const mihomoUpgrade = async (): Promise<void> => {
const instance = await getAxios() return await mihomoHttp('POST', '/upgrade')
return await instance.post('/upgrade')
} }
export const startMihomoTraffic = async (): Promise<void> => { export const startMihomoTraffic = async (): Promise<void> => {
await mihomoTraffic() await mihomoTraffic()
} }
export const stopMihomoTraffic = (): void => { export const stopMihomoTraffic = async (): Promise<void> => {
if (mihomoTrafficWs) { if (mihomoTrafficWs) {
mihomoTrafficWs.removeAllListeners() mihomoTrafficWs.end()
if (mihomoTrafficWs.readyState === WebSocket.OPEN) {
mihomoTrafficWs.close()
}
mihomoTrafficWs = null mihomoTrafficWs = null
} }
} }
const mihomoTraffic = async (): Promise<void> => { const mihomoTraffic = async (): Promise<void> => {
const controledMihomoConfig = await getControledMihomoConfig()
let server = controledMihomoConfig['external-controller']
const secret = controledMihomoConfig.secret ?? ''
if (server?.startsWith(':')) server = `127.0.0.1${server}`
stopMihomoTraffic() stopMihomoTraffic()
mihomoTrafficWs = await mihomoWs('/traffic')
mihomoTrafficWs = new WebSocket(`ws://${server}/traffic?token=${encodeURIComponent(secret)}`) mihomoTrafficWs.on('data', (data) => {
mihomoTrafficWs.onmessage = async (e): Promise<void> => {
const data = e.data as string
const json = JSON.parse(data) as IMihomoTrafficInfo
trafficRetry = 10
try { try {
const json = JSON.parse(trimJson(data.toString())) as IMihomoTrafficInfo
trafficRetry = 10
mainWindow?.webContents.send('mihomoTraffic', json) mainWindow?.webContents.send('mihomoTraffic', json)
if (process.platform !== 'linux') { tray?.setToolTip(
tray?.setToolTip( '↑' +
'↑' + `${calcTraffic(json.up)}/s`.padStart(9) +
`${calcTraffic(json.up)}/s`.padStart(9) + '\n↓' +
'\n↓' + `${calcTraffic(json.down)}/s`.padStart(9)
`${calcTraffic(json.down)}/s`.padStart(9) )
)
}
} catch { } catch {
// ignore // ignore
} }
} })
mihomoTrafficWs.on('close', () => {
mihomoTrafficWs.onclose = (): void => {
if (trafficRetry) { if (trafficRetry) {
trafficRetry-- trafficRetry--
mihomoTraffic() mihomoTraffic()
} }
} })
mihomoTrafficWs.onerror = (): void => { mihomoTrafficWs.on('error', (): void => {
if (mihomoTrafficWs) { stopMihomoTraffic()
mihomoTrafficWs.close() })
mihomoTrafficWs = null
}
}
} }
export const startMihomoMemory = async (): Promise<void> => { export const startMihomoMemory = async (): Promise<void> => {
await mihomoMemory() await mihomoMemory()
} }
export const stopMihomoMemory = (): void => { export const stopMihomoMemory = async (): Promise<void> => {
if (mihomoMemoryWs) { if (mihomoMemoryWs) {
mihomoMemoryWs.removeAllListeners() mihomoMemoryWs.end()
if (mihomoMemoryWs.readyState === WebSocket.OPEN) {
mihomoMemoryWs.close()
}
mihomoMemoryWs = null mihomoMemoryWs = null
} }
} }
const mihomoMemory = async (): Promise<void> => { const mihomoMemory = async (): Promise<void> => {
const controledMihomoConfig = await getControledMihomoConfig()
let server = controledMihomoConfig['external-controller']
const secret = controledMihomoConfig.secret ?? ''
if (server?.startsWith(':')) server = `127.0.0.1${server}`
stopMihomoMemory() stopMihomoMemory()
mihomoMemoryWs = await mihomoWs('/memory')
mihomoMemoryWs = new WebSocket(`ws://${server}/memory?token=${encodeURIComponent(secret)}`) mihomoMemoryWs.on('data', (data) => {
mihomoMemoryWs.onmessage = (e): void => {
const data = e.data as string
memoryRetry = 10
try { try {
mainWindow?.webContents.send('mihomoMemory', JSON.parse(data) as IMihomoMemoryInfo) const json = JSON.parse(trimJson(data.toString())) as IMihomoMemoryInfo
memoryRetry = 10
mainWindow?.webContents.send('mihomoMemory', json)
} catch { } catch {
// ignore // ignore
} }
} })
mihomoMemoryWs.on('close', () => {
mihomoMemoryWs.onclose = (): void => {
if (memoryRetry) { if (memoryRetry) {
memoryRetry-- memoryRetry--
mihomoMemory() mihomoMemory()
} }
} })
mihomoMemoryWs.onerror = (): void => { mihomoMemoryWs.on('error', (): void => {
if (mihomoMemoryWs) { stopMihomoMemory()
mihomoMemoryWs.close() })
mihomoMemoryWs = null
}
}
} }
export const startMihomoLogs = async (): Promise<void> => { export const startMihomoLogs = async (): Promise<void> => {
await mihomoLogs() await mihomoLogs()
} }
export const stopMihomoLogs = (): void => { export const stopMihomoLogs = async (): Promise<void> => {
if (mihomoLogsWs) { if (mihomoLogsWs) {
mihomoLogsWs.removeAllListeners() mihomoLogsWs.end()
if (mihomoLogsWs.readyState === WebSocket.OPEN) {
mihomoLogsWs.close()
}
mihomoLogsWs = null mihomoLogsWs = null
} }
} }
const mihomoLogs = async (): Promise<void> => { const mihomoLogs = async (): Promise<void> => {
const controledMihomoConfig = await getControledMihomoConfig()
const { secret = '', 'log-level': level = 'info' } = controledMihomoConfig
let { 'external-controller': server } = controledMihomoConfig
if (server?.startsWith(':')) server = `127.0.0.1${server}`
stopMihomoLogs() stopMihomoLogs()
mihomoLogsWs = await mihomoWs('/logs')
mihomoLogsWs = new WebSocket( mihomoLogsWs.on('data', (data) => {
`ws://${server}/logs?token=${encodeURIComponent(secret)}&level=${level}`
)
mihomoLogsWs.onmessage = (e): void => {
const data = e.data as string
logsRetry = 10
try { try {
mainWindow?.webContents.send('mihomoLogs', JSON.parse(data) as IMihomoLogInfo) const json = JSON.parse(trimJson(data.toString())) as IMihomoLogInfo
logsRetry = 10
mainWindow?.webContents.send('mihomoLogs', json)
} catch { } catch {
// ignore // ignore
} }
} })
mihomoLogsWs.on('close', () => {
mihomoLogsWs.onclose = (): void => {
if (logsRetry) { if (logsRetry) {
logsRetry-- logsRetry--
mihomoLogs() mihomoLogs()
} }
} })
mihomoLogsWs.onerror = (): void => { mihomoLogsWs.on('error', (): void => {
if (mihomoLogsWs) { stopMihomoLogs()
mihomoLogsWs.close() })
mihomoLogsWs = null
}
}
} }
export const startMihomoConnections = async (): Promise<void> => { export const startMihomoConnections = async (): Promise<void> => {
await mihomoConnections() await mihomoConnections()
} }
export const stopMihomoConnections = (): void => { export const stopMihomoConnections = async (): Promise<void> => {
if (mihomoConnectionsWs) { if (mihomoConnectionsWs) {
mihomoConnectionsWs.removeAllListeners() mihomoConnectionsWs.end()
if (mihomoConnectionsWs.readyState === WebSocket.OPEN) {
mihomoConnectionsWs.close()
}
mihomoConnectionsWs = null mihomoConnectionsWs = null
} }
} }
const mihomoConnections = async (): Promise<void> => { const mihomoConnections = async (): Promise<void> => {
const controledMihomoConfig = await getControledMihomoConfig()
let server = controledMihomoConfig['external-controller']
const secret = controledMihomoConfig.secret ?? ''
if (server?.startsWith(':')) server = `127.0.0.1${server}`
stopMihomoConnections() stopMihomoConnections()
mihomoConnectionsWs = await mihomoWs('/connections')
mihomoConnectionsWs = new WebSocket( mihomoConnectionsWs.on('data', (data) => {
`ws://${server}/connections?token=${encodeURIComponent(secret)}`
)
mihomoConnectionsWs.onmessage = (e): void => {
const data = e.data as string
connectionsRetry = 10
try { try {
mainWindow?.webContents.send('mihomoConnections', JSON.parse(data) as IMihomoConnectionsInfo) const json = JSON.parse(trimJson(data.toString())) as IMihomoConnectionsInfo
connectionsRetry = 10
mainWindow?.webContents.send('mihomoConnections', json)
} catch { } catch {
// ignore // ignore
} }
} })
mihomoConnectionsWs.on('close', () => {
mihomoConnectionsWs.onclose = (): void => {
if (connectionsRetry) { if (connectionsRetry) {
connectionsRetry-- connectionsRetry--
mihomoConnections() mihomoConnections()
} }
} })
mihomoConnectionsWs.onerror = (): void => { mihomoConnectionsWs.on('error', (): void => {
if (mihomoConnectionsWs) { stopMihomoConnections()
mihomoConnectionsWs.close() })
mihomoConnectionsWs = null
}
}
} }

View File

@ -151,6 +151,8 @@ async function migration(): Promise<void> {
useSubStore = true useSubStore = true
} = await getAppConfig() } = await getAppConfig()
const { const {
'external-controller-pipe': externalControllerPipe,
'external-controller-unix': externalControllerUnix,
'skip-auth-prefixes': skipAuthPrefixes, 'skip-auth-prefixes': skipAuthPrefixes,
authentication, authentication,
'bind-address': bindAddress, 'bind-address': bindAddress,
@ -189,6 +191,16 @@ async function migration(): Promise<void> {
if (typeof envType === 'string') { if (typeof envType === 'string') {
await patchAppConfig({ envType: [envType] }) await patchAppConfig({ envType: [envType] })
} }
// use unix socket
if (process.platform !== 'win32' && !externalControllerUnix) {
await patchControledMihomoConfig({ 'external-controller-unix': 'mihomo-party.sock' })
}
// use named pipe
if (process.platform === 'win32' && !externalControllerPipe) {
await patchControledMihomoConfig({
'external-controller-pipe': '\\\\.\\pipe\\MihomoParty\\mihomo'
})
}
} }
function initDeeplink(): void { function initDeeplink(): void {

View File

@ -36,8 +36,8 @@ export const defaultConfig: IAppConfig = {
} }
export const defaultControledMihomoConfig: Partial<IMihomoConfig> = { export const defaultControledMihomoConfig: Partial<IMihomoConfig> = {
'external-controller': '127.0.0.1:9090', 'external-controller-pipe': '\\\\.pipe\\MihomoParty\\mihomo',
secret: '', 'external-controller-unix': 'mihomo-party.sock',
ipv6: true, ipv6: true,
mode: 'rule', mode: 'rule',
'mixed-port': 7890, 'mixed-port': 7890,

View File

@ -343,6 +343,8 @@ interface IMihomoProfileConfig {
} }
interface IMihomoConfig { interface IMihomoConfig {
'external-controller-pipe': string
'external-controller-unix': string
'external-controller': string 'external-controller': string
secret?: string secret?: string
ipv6: boolean ipv6: boolean