Explorar el Código

feat: Support PAC Mode

MystiPanda hace 1 año
padre
commit
212021c878

+ 1 - 1
src-tauri/Cargo.lock

@@ -5169,7 +5169,7 @@ dependencies = [
 [[package]]
 name = "sysproxy"
 version = "0.3.0"
-source = "git+https://github.com/zzzgydi/sysproxy-rs?branch=main#1402eba27022a2da7b91b3090d2f4936b6260f10"
+source = "git+https://github.com/zzzgydi/sysproxy-rs?branch=main#24e8d46cb338a6a8e28742dea6ed993cd6450780"
 dependencies = [
  "interfaces",
  "iptools",

+ 13 - 2
src-tauri/src/cmds.rs

@@ -8,7 +8,7 @@ use crate::{ret_err, wrap_err};
 use anyhow::{Context, Result};
 use serde_yaml::Mapping;
 use std::collections::{HashMap, VecDeque};
-use sysproxy::Sysproxy;
+use sysproxy::{Autoproxy, Sysproxy};
 use tauri::{api, Manager};
 type CmdResult<T = ()> = Result<T, String>;
 
@@ -194,7 +194,6 @@ pub fn grant_permission(_core: String) -> CmdResult {
 #[tauri::command]
 pub fn get_sys_proxy() -> CmdResult<Mapping> {
     let current = wrap_err!(Sysproxy::get_system_proxy())?;
-
     let mut map = Mapping::new();
     map.insert("enable".into(), current.enable.into());
     map.insert(
@@ -206,6 +205,18 @@ pub fn get_sys_proxy() -> CmdResult<Mapping> {
     Ok(map)
 }
 
+/// get the system proxy
+#[tauri::command]
+pub fn get_auto_proxy() -> CmdResult<Mapping> {
+    let current = wrap_err!(Autoproxy::get_auto_proxy())?;
+
+    let mut map = Mapping::new();
+    map.insert("enable".into(), current.enable.into());
+    map.insert("url".into(), current.url.into());
+
+    Ok(map)
+}
+
 #[tauri::command]
 pub fn get_clash_logs() -> CmdResult<VecDeque<String>> {
     Ok(logger::Logger::global().get_log())

+ 5 - 0
src-tauri/src/config/mod.rs

@@ -13,3 +13,8 @@ pub use self::prfitem::*;
 pub use self::profiles::*;
 pub use self::runtime::*;
 pub use self::verge::*;
+
+pub const DEFAULT_PAC: &str = r#"function FindProxyForURL(url, host) {
+  return "PROXY 127.0.0.1:%mixed-port%; SOCKS5 127.0.0.1:%mixed-port%; DIRECT;"
+}
+"#;

+ 11 - 0
src-tauri/src/config/verge.rs

@@ -1,3 +1,4 @@
+use crate::config::DEFAULT_PAC;
 use crate::utils::{dirs, help};
 use anyhow::Result;
 use log::LevelFilter;
@@ -80,6 +81,12 @@ pub struct IVerge {
     /// proxy guard duration
     pub proxy_guard_duration: Option<u64>,
 
+    /// use pac mode
+    pub proxy_auto_config: Option<bool>,
+
+    /// pac script content
+    pub pac_file_content: Option<String>,
+
     /// theme setting
     pub theme_setting: Option<IVergeTheme>,
 
@@ -211,6 +218,8 @@ impl IVerge {
             enable_auto_launch: Some(false),
             enable_silent_start: Some(false),
             enable_system_proxy: Some(false),
+            proxy_auto_config: Some(false),
+            pac_file_content: Some(DEFAULT_PAC.into()),
             enable_random_port: Some(false),
             #[cfg(not(target_os = "windows"))]
             verge_redir_port: Some(7895),
@@ -290,6 +299,8 @@ impl IVerge {
         patch!(enable_proxy_guard);
         patch!(system_proxy_bypass);
         patch!(proxy_guard_duration);
+        patch!(proxy_auto_config);
+        patch!(pac_file_content);
 
         patch!(theme_setting);
         patch!(web_ui_list);

+ 113 - 37
src-tauri/src/core/sysopt.rs

@@ -1,11 +1,14 @@
-use crate::{config::Config, log_err};
+use crate::{
+    config::{Config, IVerge},
+    log_err,
+};
 use anyhow::{anyhow, Result};
 use auto_launch::{AutoLaunch, AutoLaunchBuilder};
 use once_cell::sync::OnceCell;
 use parking_lot::Mutex;
 use std::env::current_exe;
 use std::sync::Arc;
-use sysproxy::Sysproxy;
+use sysproxy::{Autoproxy, Sysproxy};
 use tauri::async_runtime::Mutex as TokioMutex;
 
 pub struct Sysopt {
@@ -16,6 +19,13 @@ pub struct Sysopt {
     /// recover it when exit
     old_sysproxy: Arc<Mutex<Option<Sysproxy>>>,
 
+    /// current auto proxy setting
+    cur_autoproxy: Arc<Mutex<Option<Autoproxy>>>,
+
+    /// record the original auto proxy
+    /// recover it when exit
+    old_autoproxy: Arc<Mutex<Option<Autoproxy>>>,
+
     /// helps to auto launch the app
     auto_launch: Arc<Mutex<Option<AutoLaunch>>>,
 
@@ -38,6 +48,8 @@ impl Sysopt {
         SYSOPT.get_or_init(|| Sysopt {
             cur_sysproxy: Arc::new(Mutex::new(None)),
             old_sysproxy: Arc::new(Mutex::new(None)),
+            cur_autoproxy: Arc::new(Mutex::new(None)),
+            old_autoproxy: Arc::new(Mutex::new(None)),
             auto_launch: Arc::new(Mutex::new(None)),
             guard_state: Arc::new(TokioMutex::new(false)),
         })
@@ -49,38 +61,77 @@ impl Sysopt {
             .latest()
             .verge_mixed_port
             .unwrap_or(Config::clash().data().get_mixed_port());
+        let pac_port = IVerge::get_singleton_port();
 
-        let (enable, bypass) = {
+        let (enable, bypass, pac) = {
             let verge = Config::verge();
             let verge = verge.latest();
             (
                 verge.enable_system_proxy.unwrap_or(false),
                 verge.system_proxy_bypass.clone(),
+                verge.proxy_auto_config.unwrap_or(false),
             )
         };
-
-        let current = Sysproxy {
-            enable,
-            host: String::from("127.0.0.1"),
-            port,
-            bypass: match bypass {
-                Some(bypass) => {
-                    if bypass.is_empty() {
-                        DEFAULT_BYPASS.into()
-                    } else {
-                        bypass
+        if pac {
+            let sys = Sysproxy {
+                enable: false,
+                host: String::from("127.0.0.1"),
+                port,
+                bypass: match bypass {
+                    Some(bypass) => {
+                        if bypass.is_empty() {
+                            DEFAULT_BYPASS.into()
+                        } else {
+                            bypass
+                        }
                     }
-                }
-                None => DEFAULT_BYPASS.into(),
-            },
-        };
+                    None => DEFAULT_BYPASS.into(),
+                },
+            };
+            let old = Sysproxy::get_system_proxy().ok();
+            sys.set_system_proxy()?;
 
-        if enable {
+            *self.old_sysproxy.lock() = old;
+            *self.cur_sysproxy.lock() = Some(sys);
+            let auto = Autoproxy {
+                enable,
+                url: format!("http://127.0.0.1:{pac_port}/commands/pac"),
+            };
+            let old = Autoproxy::get_auto_proxy().ok();
+            auto.set_auto_proxy()?;
+
+            *self.old_autoproxy.lock() = old;
+            *self.cur_autoproxy.lock() = Some(auto);
+        } else {
+            let auto = Autoproxy {
+                enable: false,
+                url: String::new(),
+            };
+            let old = Autoproxy::get_auto_proxy().ok();
+            auto.set_auto_proxy()?;
+
+            *self.old_autoproxy.lock() = old;
+            *self.cur_autoproxy.lock() = Some(auto);
+            let sys = Sysproxy {
+                enable,
+                host: String::from("127.0.0.1"),
+                port,
+                bypass: match bypass {
+                    Some(bypass) => {
+                        if bypass.is_empty() {
+                            DEFAULT_BYPASS.into()
+                        } else {
+                            bypass
+                        }
+                    }
+                    None => DEFAULT_BYPASS.into(),
+                },
+            };
             let old = Sysproxy::get_system_proxy().ok();
-            current.set_system_proxy()?;
+            sys.set_system_proxy()?;
 
             *self.old_sysproxy.lock() = old;
-            *self.cur_sysproxy.lock() = Some(current);
+            *self.cur_sysproxy.lock() = Some(sys);
         }
 
         // run the system proxy guard
@@ -92,24 +143,38 @@ impl Sysopt {
     pub fn update_sysproxy(&self) -> Result<()> {
         let mut cur_sysproxy = self.cur_sysproxy.lock();
         let old_sysproxy = self.old_sysproxy.lock();
+        let mut cur_autoproxy = self.cur_autoproxy.lock();
+        let old_autoproxy = self.old_autoproxy.lock();
 
-        if cur_sysproxy.is_none() || old_sysproxy.is_none() {
-            drop(cur_sysproxy);
-            drop(old_sysproxy);
-            return self.init_sysproxy();
-        }
-
-        let (enable, bypass) = {
+        let (enable, bypass, pac) = {
             let verge = Config::verge();
             let verge = verge.latest();
             (
                 verge.enable_system_proxy.unwrap_or(false),
                 verge.system_proxy_bypass.clone(),
+                verge.proxy_auto_config.unwrap_or(false),
             )
         };
-        let mut sysproxy = cur_sysproxy.take().unwrap();
+        if pac {
+            if cur_autoproxy.is_none() || old_autoproxy.is_none() {
+                drop(cur_autoproxy);
+                drop(old_autoproxy);
+                return self.init_sysproxy();
+            }
+        } else {
+            if cur_sysproxy.is_none() || old_sysproxy.is_none() {
+                drop(cur_sysproxy);
+                drop(old_sysproxy);
+                return self.init_sysproxy();
+            }
+        }
+        let port = Config::verge()
+            .latest()
+            .verge_mixed_port
+            .unwrap_or(Config::clash().data().get_mixed_port());
+        let pac_port = IVerge::get_singleton_port();
 
-        sysproxy.enable = enable;
+        let mut sysproxy = cur_sysproxy.take().unwrap();
         sysproxy.bypass = match bypass {
             Some(bypass) => {
                 if bypass.is_empty() {
@@ -120,15 +185,26 @@ impl Sysopt {
             }
             None => DEFAULT_BYPASS.into(),
         };
-
-        let port = Config::verge()
-            .latest()
-            .verge_mixed_port
-            .unwrap_or(Config::clash().data().get_mixed_port());
         sysproxy.port = port;
 
-        sysproxy.set_system_proxy()?;
-        *cur_sysproxy = Some(sysproxy);
+        let mut autoproxy = cur_autoproxy.take().unwrap();
+        autoproxy.url = format!("http://127.0.0.1:{pac_port}/commands/pac");
+
+        if pac {
+            sysproxy.enable = false;
+            sysproxy.set_system_proxy()?;
+            *cur_sysproxy = Some(sysproxy);
+            autoproxy.enable = enable;
+            autoproxy.set_auto_proxy()?;
+            *cur_autoproxy = Some(autoproxy);
+        } else {
+            autoproxy.enable = false;
+            autoproxy.set_auto_proxy()?;
+            *cur_autoproxy = Some(autoproxy);
+            sysproxy.enable = enable;
+            sysproxy.set_system_proxy()?;
+            *cur_sysproxy = Some(sysproxy);
+        }
 
         Ok(())
     }

+ 8 - 1
src-tauri/src/feat.rs

@@ -177,6 +177,8 @@ pub async fn patch_verge(patch: IVerge) -> Result<()> {
     let tun_mode = patch.enable_tun_mode;
     let auto_launch = patch.enable_auto_launch;
     let system_proxy = patch.enable_system_proxy;
+    let pac = patch.proxy_auto_config;
+    let pac_content = patch.pac_file_content;
     let proxy_bypass = patch.system_proxy_bypass;
     let language = patch.language;
     let port = patch.verge_mixed_port;
@@ -219,7 +221,12 @@ pub async fn patch_verge(patch: IVerge) -> Result<()> {
         if auto_launch.is_some() {
             sysopt::Sysopt::global().update_launch()?;
         }
-        if system_proxy.is_some() || proxy_bypass.is_some() || port.is_some() {
+        if system_proxy.is_some()
+            || proxy_bypass.is_some()
+            || port.is_some()
+            || pac.is_some()
+            || pac_content.is_some()
+        {
             sysopt::Sysopt::global().update_sysproxy()?;
             sysopt::Sysopt::global().guard_proxy();
         }

+ 1 - 0
src-tauri/src/main.rs

@@ -36,6 +36,7 @@ fn main() -> std::io::Result<()> {
         .invoke_handler(tauri::generate_handler![
             // common
             cmds::get_sys_proxy,
+            cmds::get_auto_proxy,
             cmds::open_app_dir,
             cmds::open_logs_dir,
             cmds::open_web_url,

+ 14 - 2
src-tauri/src/utils/server.rs

@@ -1,7 +1,7 @@
 extern crate warp;
 
 use super::resolve;
-use crate::config::IVerge;
+use crate::config::{Config, IVerge, DEFAULT_PAC};
 use anyhow::{bail, Result};
 use port_scanner::local_port_available;
 use std::convert::Infallible;
@@ -64,6 +64,18 @@ pub fn embed_server(app_handle: AppHandle) {
             "ok"
         });
 
+        let pac = warp::path!("commands" / "pac").map(move || {
+            let content = Config::verge()
+                .latest()
+                .pac_file_content
+                .clone()
+                .unwrap_or(DEFAULT_PAC.to_string());
+            let port = Config::verge()
+                .latest()
+                .verge_mixed_port
+                .unwrap_or(Config::clash().data().get_mixed_port());
+            content.replace("%mixed-port%", &format!("{}", port))
+        });
         let scheme = warp::path!("commands" / "scheme")
             .and(warp::query::<QueryParam>())
             .and_then(scheme_handler);
@@ -72,7 +84,7 @@ pub fn embed_server(app_handle: AppHandle) {
             resolve::resolve_scheme(query.param).await;
             Ok("ok")
         }
-        let commands = ping.or(visible).or(scheme);
+        let commands = ping.or(visible).or(pac).or(scheme);
         warp::serve(commands).run(([127, 0, 0, 1], port)).await;
     });
 }

+ 123 - 41
src/components/setting/mods/sysproxy-viewer.tsx

@@ -10,23 +10,34 @@ import {
   styled,
   TextField,
   Typography,
+  Button,
 } from "@mui/material";
 import { useVerge } from "@/hooks/use-verge";
-import { getSystemProxy } from "@/services/cmds";
+import { getSystemProxy, getAutotemProxy } from "@/services/cmds";
 import { BaseDialog, DialogRef, Notice, Switch } from "@/components/base";
+import { Edit } from "@mui/icons-material";
+import { EditorViewer } from "@/components/profile/editor-viewer";
+const DEFAULT_PAC = `function FindProxyForURL(url, host) {
+  return "PROXY 127.0.0.1:%mixed-port%; SOCKS5 127.0.0.1:%mixed-port%; DIRECT;"
+}`;
 
 export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
   const { t } = useTranslation();
 
   const [open, setOpen] = useState(false);
-
+  const [editorOpen, setEditorOpen] = useState(false);
   const { verge, patchVerge } = useVerge();
 
   type SysProxy = Awaited<ReturnType<typeof getSystemProxy>>;
   const [sysproxy, setSysproxy] = useState<SysProxy>();
 
+  type AutoProxy = Awaited<ReturnType<typeof getAutotemProxy>>;
+  const [autoproxy, setAutoproxy] = useState<AutoProxy>();
+
   const {
     enable_system_proxy: enabled,
+    proxy_auto_config,
+    pac_file_content,
     enable_proxy_guard,
     system_proxy_bypass,
     proxy_guard_duration,
@@ -36,6 +47,8 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
     guard: enable_proxy_guard,
     bypass: system_proxy_bypass,
     duration: proxy_guard_duration ?? 10,
+    pac: proxy_auto_config,
+    pac_content: pac_file_content ?? DEFAULT_PAC,
   });
 
   useImperativeHandle(ref, () => ({
@@ -45,8 +58,11 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
         guard: enable_proxy_guard,
         bypass: system_proxy_bypass,
         duration: proxy_guard_duration ?? 10,
+        pac: proxy_auto_config,
+        pac_content: pac_file_content ?? DEFAULT_PAC,
       });
       getSystemProxy().then((p) => setSysproxy(p));
+      getAutotemProxy().then((p) => setAutoproxy(p));
     },
     close: () => setOpen(false),
   }));
@@ -68,6 +84,12 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
     if (value.bypass !== system_proxy_bypass) {
       patch.system_proxy_bypass = value.bypass;
     }
+    if (value.pac !== proxy_auto_config) {
+      patch.proxy_auto_config = value.pac;
+    }
+    if (value.pac_content !== pac_file_content) {
+      patch.pac_file_content = value.pac_content;
+    }
 
     try {
       await patchVerge(patch);
@@ -89,6 +111,15 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
       onOk={onSave}
     >
       <List>
+        <ListItem sx={{ padding: "5px 2px" }}>
+          <ListItemText primary={t("Use PAC Mode")} />
+          <Switch
+            edge="end"
+            disabled={!enabled}
+            checked={value.pac}
+            onChange={(_, e) => setValue((v) => ({ ...v, pac: e }))}
+          />
+        </ListItem>
         <ListItem sx={{ padding: "5px 2px" }}>
           <ListItemText primary={t("Proxy Guard")} />
           <Switch
@@ -117,25 +148,63 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
             }}
           />
         </ListItem>
-
-        <ListItem sx={{ padding: "5px 2px", alignItems: "start" }}>
-          <ListItemText primary={t("Proxy Bypass")} sx={{ padding: "3px 0" }} />
-        </ListItem>
-        <ListItem sx={{ padding: "5px 2px" }}>
-          <TextField
-            disabled={!enabled}
-            size="small"
-            autoComplete="off"
-            multiline
-            rows={4}
-            sx={{ width: "100%" }}
-            value={value.bypass}
-            placeholder={sysproxy?.bypass || `-`}
-            onChange={(e) =>
-              setValue((v) => ({ ...v, bypass: e.target.value }))
-            }
-          />
-        </ListItem>
+        {!value.pac && (
+          <>
+            <ListItem sx={{ padding: "5px 2px", alignItems: "start" }}>
+              <ListItemText
+                primary={t("Proxy Bypass")}
+                sx={{ padding: "3px 0" }}
+              />
+            </ListItem>
+            <ListItem sx={{ padding: "5px 2px" }}>
+              <TextField
+                disabled={!enabled}
+                size="small"
+                autoComplete="off"
+                multiline
+                rows={4}
+                sx={{ width: "100%" }}
+                value={value.bypass}
+                placeholder={sysproxy?.bypass || `-`}
+                onChange={(e) =>
+                  setValue((v) => ({ ...v, bypass: e.target.value }))
+                }
+              />
+            </ListItem>
+          </>
+        )}
+        {value.pac && (
+          <>
+            <ListItem sx={{ padding: "5px 2px", alignItems: "start" }}>
+              <ListItemText
+                primary={t("PAC Script Content")}
+                sx={{ padding: "3px 0" }}
+              />
+              <Button
+                startIcon={<Edit />}
+                variant="outlined"
+                onClick={() => {
+                  setEditorOpen(true);
+                }}
+              >
+                {t("Edit")} PAC
+              </Button>
+              <EditorViewer
+                title={`${t("Edit")} PAC`}
+                mode="text"
+                property={value.pac_content ?? ""}
+                open={editorOpen}
+                language="javascript"
+                onChange={(content) => {
+                  setValue((v) => ({ ...v, pac_content: content ?? "" }));
+                }}
+                onClose={() => {
+                  setEditorOpen(false);
+                }}
+              />
+            </ListItem>
+          </>
+        )}
       </List>
 
       <Box sx={{ mt: 2.5 }}>
@@ -146,29 +215,42 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
         <FlexBox>
           <Typography className="label">{t("Enable status")}</Typography>
           <Typography className="value">
-            {(!!sysproxy?.enable).toString()}
+            {value.pac
+              ? (!!autoproxy?.enable).toString()
+              : (!!sysproxy?.enable).toString()}
           </Typography>
         </FlexBox>
+        {!value.pac && (
+          <>
+            <FlexBox>
+              <Typography className="label">{t("Server Addr")}</Typography>
+              <Typography className="value">
+                {sysproxy?.server || "-"}
+              </Typography>
+            </FlexBox>
 
-        <FlexBox>
-          <Typography className="label">{t("Server Addr")}</Typography>
-          <Typography className="value">{sysproxy?.server || "-"}</Typography>
-        </FlexBox>
-
-        <FlexBox>
-          <Typography className="label">{t("Bypass")}</Typography>
-        </FlexBox>
-        <FlexBox>
-          <TextField
-            disabled={true}
-            size="small"
-            autoComplete="off"
-            multiline
-            rows={4}
-            sx={{ width: "100%" }}
-            value={sysproxy?.bypass || "-"}
-          />
-        </FlexBox>
+            <FlexBox>
+              <Typography className="label">{t("Bypass")}</Typography>
+            </FlexBox>
+            <FlexBox>
+              <TextField
+                disabled={true}
+                size="small"
+                autoComplete="off"
+                multiline
+                rows={4}
+                sx={{ width: "100%" }}
+                value={sysproxy?.bypass || "-"}
+              />
+            </FlexBox>
+          </>
+        )}
+        {value.pac && (
+          <FlexBox>
+            <Typography className="label">{t("PAC URL")}</Typography>
+            <Typography className="value">{autoproxy?.url || "-"}</Typography>
+          </FlexBox>
+        )}
       </Box>
     </BaseDialog>
   );

+ 3 - 0
src/locales/en.json

@@ -118,6 +118,9 @@
   "System Proxy Setting": "System Proxy Setting",
   "Open UWP tool": "Open UWP tool",
   "Update GeoData": "Update GeoData",
+  "Use PAC Mode": "Use PAC Mode",
+  "PAC URL": "PAC URL",
+  "PAC Script Content": "PAC Script Content",
   "Proxy Guard": "Proxy Guard",
   "Guard Duration": "Guard Duration",
   "Proxy Bypass": "Proxy Bypass",

+ 3 - 0
src/locales/fa.json

@@ -118,6 +118,9 @@
   "System Proxy Setting": "تنظیمات پراکسی سیستم",
   "Open UWP tool": "باز کردن ابزار UWP",
   "Update GeoData": "به‌روزرسانی GeoData",
+  "Use PAC Mode": "استفاده از حالت PAC",
+  "PAC URL": "PAC URL",
+  "PAC Script Content": "محتوای اسکریپت PAC",
   "Proxy Guard": "محافظ پراکسی",
   "Guard Duration": "مدت محافظت",
   "Proxy Bypass": "دور زدن پراکسی",

+ 3 - 0
src/locales/ru.json

@@ -118,6 +118,9 @@
   "System Proxy Setting": "Настройка системного прокси",
   "Open UWP tool": "Открыть UWP инструмент",
   "Update GeoData": "Обновление GeoData",
+  "Use PAC Mode": "Используйте режим PAC.",
+  "PAC URL": "Адрес PAC",
+  "PAC Script Content": "Содержание сценария PAC",
   "Proxy Guard": "Защита прокси",
   "Guard Duration": "Период защиты",
   "Proxy Bypass": "Игнорирование прокси",

+ 3 - 0
src/locales/zh.json

@@ -118,6 +118,9 @@
   "System Proxy Setting": "系统代理设置",
   "Open UWP tool": "UWP 工具",
   "Update GeoData": "更新 GeoData",
+  "Use PAC Mode": "使用PAC模式",
+  "PAC URL": "PAC 地址",
+  "PAC Script Content": "PAC 脚本内容",
   "Proxy Guard": "系统代理守卫",
   "Guard Duration": "代理守卫间隔",
   "Proxy Bypass": "代理绕过",

+ 7 - 0
src/services/cmds.ts

@@ -127,6 +127,13 @@ export async function getSystemProxy() {
   }>("get_sys_proxy");
 }
 
+export async function getAutotemProxy() {
+  return invoke<{
+    enable: boolean;
+    url: string;
+  }>("get_auto_proxy");
+}
+
 export async function changeClashCore(clashCore: string) {
   return invoke<any>("change_clash_core", { clashCore });
 }

+ 2 - 0
src/services/types.d.ts

@@ -216,6 +216,8 @@ interface IVergeConfig {
   enable_service_mode?: boolean;
   enable_silent_start?: boolean;
   enable_system_proxy?: boolean;
+  proxy_auto_config?: boolean;
+  pac_file_content?: string;
   enable_random_port?: boolean;
   verge_mixed_port?: number;
   verge_socks_port?: number;