import { invoke } from '@tauri-apps/api/core' import dayjs from 'dayjs' import type { CommandFailure } from '@/services/notice-service' import { showNotice } from '@/services/notice-service' import type { ProxyViewV1 } from '@/types/proxy-view' import { debugLog } from '@/utils/debug' export async function copyClashEnv() { return invoke('copy_clash_env') } export async function getProfiles() { return invoke('get_profiles') } export async function enhanceProfiles() { return ( (await invoke('enhance_profiles')).status === 'valid' ) } export async function patchProfilesConfig(profiles: IProfilesConfig) { return invoke('patch_profiles_config', { profiles }) } export async function createProfile( item: Partial, fileData?: string | null, ) { return invoke('create_profile', { item, fileData }) } export async function viewProfile(index: string) { return invoke('view_profile', { index }) } export async function readProfileFile(index: string) { return invoke('read_profile_file', { index }) } export async function saveProfileFile(index: string, fileData: string) { return ( ( await invoke('save_profile_file', { index, fileData, }) ).status === 'valid' ) } export async function importProfile(url: string, option?: IProfileOption) { return invoke('import_profile', { url, option: option || { with_proxy: true }, }) } export async function reorderProfile(activeId: string, overId: string) { return invoke('reorder_profile', { activeId, overId, }) } export async function updateProfile(index: string, option?: IProfileOption) { return invoke('update_profile', { index, option }) } export async function deleteProfile(index: string) { return invoke('delete_profile', { index }) } export async function patchProfile( index: string, profile: Partial, ) { return invoke('patch_profile', { index, profile }) } export async function getClashInfo() { return invoke('get_clash_info') } // Fallback mode read independent of strict mihomo `/configs` deserialization. export async function getClashMode() { return invoke('get_clash_mode') } export async function getRuntimeConfig() { return invoke('get_runtime_config') } export async function getRuntimeYaml() { return invoke('get_runtime_yaml') } export async function getRuntimeLogs() { return invoke>('get_runtime_logs') } export async function getRuntimeProxyChainConfig(proxyChainExitNode: string) { return invoke('get_runtime_proxy_chain_config', { proxyChainExitNode, }) } export async function updateProxyChainConfigInRuntime(proxyChainConfig: any) { return invoke('update_proxy_chain_config_in_runtime', { proxyChainConfig, }) } export async function patchClashConfig(payload: Partial) { return invoke('patch_clash_config', { payload }) } export async function patchClashMode(payload: string) { return invoke('patch_clash_mode', { payload }) } export async function syncTrayProxySelection() { return invoke('sync_tray_proxy_selection') } /** Sends one selection pair so the backend can merge against current profile state. */ export async function recordSelectedNode(groupName: string, node: string) { return invoke('record_selected_node', { groupName, node }) } export async function forgetSelectedNode(groupName: string) { return invoke('forget_selected_node', { groupName }) } export async function getProxyView(): Promise { const view = await invoke('get_proxy_view') if (view.schemaVersion !== 1) { throw new Error('Unsupported proxy view schema: ' + view.schemaVersion) } return view } export async function getClashLogs() { const regex = /time="(.+?)"\s+level=(.+?)\s+msg="(.+?)"/ const newRegex = /(.+?)\s+(.+?)\s+(.+)/ const logs = await invoke('get_clash_logs') return logs.reduce((acc, log) => { const result = log.match(regex) if (result) { const [_, _time, type, payload] = result const time = dayjs(_time).format('MM-DD HH:mm:ss') acc.push({ time, type, payload }) return acc } const result2 = log.match(newRegex) if (result2) { const [_, time, type, payload] = result2 acc.push({ time, type, payload }) } return acc }, []) } export async function getVergeConfig() { return invoke('get_verge_config') } export async function patchVergeConfig(payload: IVergeConfig) { return invoke('patch_verge_config', { payload }) } export async function setDnsOverride( profileUid: string, enabled: boolean, confirmation?: string, ) { return invoke< { status: 'applied' } | { status: 'confirmation_required'; source: string } >('set_dns_override', { profileUid, enabled, confirmation }) } export async function takeDnsOverrideNotice() { return invoke('take_dns_override_notice') } export async function takeServiceFallbackNotice() { return invoke('take_service_fallback_notice') } export async function takeServiceRepairNotice() { return invoke('take_service_repair_notice') } export async function takeDiscardedKeysNotice() { return invoke('take_discarded_keys_notice') } export async function getSystemProxy() { return invoke<{ enable: boolean server: string bypass: string }>('get_sys_proxy') } export async function getAutotemProxy() { try { debugLog('[API] 开始调用 get_auto_proxy') const result = await invoke<{ enable: boolean url: string }>('get_auto_proxy') debugLog('[API] get_auto_proxy 调用成功:', result) return result } catch (error) { console.error('[API] get_auto_proxy 调用失败:', error) return { enable: false, url: '', } } } export async function getEmbeddedServerPort() { return invoke('get_embedded_server_port') } export async function changeClashCore(clashCore: string) { return invoke('change_clash_core', { clashCore }) } export async function restartCore() { return invoke('restart_core') } export interface CoreUpgradeReport { /** False when the managed core was already at the latest version. */ upgraded: boolean from: string to: string } export async function upgradeClashCore(force = false) { return invoke('upgrade_clash_core', { force }) } export async function restartApp() { return invoke('restart_app') } export async function getAppDir() { return invoke('get_app_dir') } export async function openAppDir() { return invoke('open_app_dir').catch((err) => showNotice.error(err)) } export async function openCoreDir() { return invoke('open_core_dir').catch((err) => showNotice.error(err)) } export async function openLogsDir() { return invoke('open_logs_dir').catch((err) => showNotice.error(err)) } export async function syncRuntimeProviders() { return invoke('sync_runtime_providers').catch((err) => { console.warn('failed to queue the provider cache sync', err) }) } export async function cmdTestDelay(url: string) { return invoke('test_delay', { url }) } export async function invoke_uwp_tool() { return invoke('invoke_uwp_tool').catch((err) => showNotice.error(err, 1500), ) } export async function openDevTools() { return invoke('open_devtools') } export async function exitApp() { return invoke('exit_app') } export async function exportDiagnosticInfo() { return invoke('export_diagnostic_info') } interface SystemInfo { system_name: string system_version: string system_kernel_version: string system_arch: string app_version: string app_core_mode: string app_is_admin: boolean } export async function getSystemInfo() { return invoke('get_system_info') } export async function copyIconFile( path: string, name: 'common' | 'sysproxy' | 'tun', ) { const key = `icon_${name}_update_time` const previousTime = localStorage.getItem(key) || '' const currentTime = String(Date.now()) localStorage.setItem(key, currentTime) const iconInfo = { name, previous_t: previousTime, current_t: currentTime, } return invoke('copy_icon_file', { path, iconInfo }) } export async function downloadIconCache(url: string, name: string) { return invoke('download_icon_cache', { url, name }) } export async function getNetworkInterfaces() { return invoke('get_network_interfaces') } export async function getSystemHostname() { return invoke('get_system_hostname') } export async function getNetworkInterfacesInfo() { return invoke('get_network_interfaces_info') } export async function createWebdavBackup() { return invoke('create_webdav_backup') } export async function createLocalBackup() { return invoke('create_local_backup') } export async function deleteWebdavBackup(filename: string) { return invoke('delete_webdav_backup', { filename }) } export async function deleteLocalBackup(filename: string) { return invoke('delete_local_backup', { filename }) } export async function restoreWebDavBackup(filename: string) { return invoke('restore_webdav_backup', { filename }) } export async function restoreLocalBackup(filename: string) { return invoke('restore_local_backup', { filename }) } export async function importLocalBackup(source: string) { return invoke('import_local_backup', { source }) } export async function exportLocalBackup(filename: string, destination: string) { return invoke('export_local_backup', { filename, destination }) } export async function saveWebdavConfig( url: string, username: string, password: string, ) { return invoke('save_webdav_config', { url, username, password, }) } export async function listWebDavBackup() { const list: IWebDavFile[] = await invoke('list_webdav_backup') list.forEach((item) => { item.filename = item.href.split('/').pop() as string }) return list } export async function listLocalBackup() { return invoke('list_local_backup') } export type RunningMode = 'Service' | 'Sidecar' | 'NotRunning' type ServiceHealth = | 'unknown' | 'ready' | 'notInstalled' | 'versionMismatch' | 'unavailable' type PendingServiceAction = | 'install' | 'uninstall' | 'reinstall' | 'forceReinstall' /** Consistent core/service snapshot with backend-derived availability flags. */ export interface RunState { mode: RunningMode service: ServiceHealth serviceUnavailableReason: string | null pendingAction: PendingServiceAction | null sidecarAllowed: boolean isAdmin: boolean opInFlight: boolean serviceUsable: boolean tunCapable: boolean serviceNeedsAttention: boolean } export const getRuntimeState = async () => { return invoke('get_runtime_state') } export type FailedOperation = | 'systemProxyEnable' | 'systemProxyDisable' | 'systemProxyRestore' | 'systemProxyGuard' export interface PendingFailure { code: string detail: string operation: FailedOperation sequence: number } export const getPendingFailures = async () => { return invoke('get_pending_failures') } export const getAppUptime = async () => { return invoke('get_app_uptime') } export const installService = async () => { return invoke('install_service') } export const uninstallService = async () => { return invoke('uninstall_service') } export const reinstallService = async () => { return invoke('reinstall_service') } export const repairService = async () => { return invoke('repair_service') } export const continueWithSidecar = async () => { return invoke('continue_with_sidecar') } export const entry_lightweight_mode = async () => { return invoke('entry_lightweight_mode') } export async function getNextUpdateTime(uid: string) { return invoke('get_next_update_time', { uid }) } interface ToggleableProxyPort { enabled: boolean port: number } export interface ProxyPortSettings { mixedPort: number socks: ToggleableProxyPort http: ToggleableProxyPort redir: ToggleableProxyPort tproxy: ToggleableProxyPort } export type ListenerTransport = 'tcp' | 'udp' export interface ListenerProbe { address: string transports: ListenerTransport[] } export type ListenerProbeOutcome = | { status: 'available' } | { status: 'conflict' port: number transport: ListenerTransport } | { status: 'invalid'; message: string } | { status: 'indeterminate'; message: string } export type SaveProxyPortsOutcome = | { status: 'saved' } | { status: 'conflict'; port: number; transport: ListenerTransport } export const probeListener = async (request: ListenerProbe) => { return invoke('probe_listener', { request }) } export const saveProxyPorts = async (settings: ProxyPortSettings) => { return invoke('save_proxy_ports', { settings }) }