text:
<?xml version="1.0" encoding="utf-8"?>
<project ver="10" name="winXray" libEmbed="true" icon="\app.ico" ui="win" output="winXray.exe" CompanyName="winXray" FileDescription="xray,xray,trojan,shadowsocks 通用客户端" LegalCopyright="Copyright (C) winXray 2020" ProductName="winXray" InternalName="winXray" FileVersion="2.3.0.0" ProductVersion="2.3.0.0" publishDir="\winXray" dstrip="false" local="false">
	<file name="main" path="main.aardio"/>
	<folder name="窗体" path="forms" comment="" embed="true" local="false">
		<folder name="main" path="forms\main" comment="forms\main" local="false" embed="false">
			<file name="pac.aardio" path="forms\main\pac.aardio" comment="forms\main\pac.aardio"/>
			<file name="xray.aardio" path="forms\main\xray.aardio" comment="forms\main\xray.aardio"/>
			<folder name="config" path="forms\main\config" comment="forms\main\config">
				<file name="advancedConfig.aardio" path="forms\main\config\advancedConfig.aardio" comment="forms\main\config\advancedConfig.aardio"/>
				<file name="jsonConfig.aardio" path="forms\main\config\jsonConfig.aardio" comment="forms\main\config\jsonConfig.aardio"/>
				<file name="subscription.aardio" path="forms\main\config\subscription.aardio" comment="forms\main\config\subscription.aardio"/>
			</folder>
			<folder name="tools" path="forms\main\tools" comment="forms\main\tools" local="false" embed="false">
				<file name="github.aardio" path="forms\main\tools\github.aardio" comment="forms\main\tools\github.aardio"/>
				<file name="tools.aardio" path="forms\main\tools\tools.aardio" comment="forms\main\tools\tools.aardio"/>
				<file name="youtube.aardio" path="forms\main\tools\youtube.aardio" comment="forms\main\tools\youtube.aardio"/>
				<file name="qr.aardio" path="forms\main\tools\qr.aardio" comment="forms\main\tools\qr.aardio"/>
				<file name="uuid.aardio" path="forms\main\tools\uuid.aardio" comment="forms\main\tools\uuid.aardio"/>
			</folder>
		</folder>
	</folder>
	<folder name="xrayCore" path="xray-core" comment="" embed="false" local="false">
		<file name="LICENSE" path="xray-core\LICENSE" comment="xray-core\LICENSE"/>
		<file name="winXray-default-servers.json" path="xray-core\winXray-default-servers.json" comment="xray-core\winXray-default-servers.json"/>
		<file name="xray.exe" path="xray-core\xray.exe" comment="xray-core\xray.exe"/>
	</folder>
</project>

mime_type: text/x-java


text:
[winXray 入门小技巧](./sub/introduce.md)
[winXray 最新视频教程](https://www.youtube.com/results?search_query=winXray&sp=CAI%253D)
[网络免费 vmess 服务器订阅链接](https://proxypool.ga/vmess/sub)
[网络免费 Shadowsocks 服务器订阅链接](https://proxypool.ga/ss/sub)
[网络免费 clash 服务器订阅链接](https://proxypoolss.tk/clash/proxies?speed=100&type=vmess,trojan)
可复制上面各种格式订阅链接，在 winXray 中点击「批量导入链接」一键体验 winXray 强大的兼容性。

# winXray
本软件源码已贡献到公共领域并放弃版权，源码可使用 [aardio（开发环境仅6.5MB）](http://www.aardio.com) 编译生成单文件绿色EXE( 不需要.Net等任何外部运行库 ），**[点这里下载](./../../raw/master/release/winXray.7z)** （ [64位版本](./../../raw/master/release/winXray.7z) / [32位版本](./../../raw/master/release/winXray32.7z) ），解压即可直接使用( 仅  **[5.0MB](./../../raw/master/release/winXray.7z)** - 已自带 Xray-core）。

winXray[:loud_sound:](http://dict.youdao.com/dictvoice?audio=winxray&type=2) 是一个简洁稳定的 [Xray/V2Ray(vmess/vless/xtls)、Shadowsocks、Trojan](https://github.com/XTLS/Xray-core) 通用客户端（Windows系统），可自动检测并连接访问速度最快的代理服务器。服务器连接异常时可以自动更换代理服务器 - 再也不用担心服务器抽风了。winXray 也提供一键安装 XRay(V2Ray、Shadowsocks、Trojan) 服务器工具。

之前我用过很多代理客户端，经常用一会就挂掉了，有些测试很久才找到下一个可用的服务器，有时怎么切换都不行，一定要把整个软件退出重启才能恢复。而且在WIN10上都有相同的BUG:PAC代理用一段时间就会卡死( winXray已经通过自行实现PAC服务器解决了这个问题 )，其实这些软件里提供的很多功能我并不需要，我只想愉快地用下 google 找点技术资料提升工作效率。但是在网上找了很久都没找到适合的软件，于是决定自己动手写一个，还好用 aardio 写软件的速度很快 - 大概用了几个小时就完成了 winXray 的主要代码，改进了几个版本以后就很稳定了，**我自己用了 winXray  几个月再也没有遇到 google 抽风访问不了的问题**。

一些网友对 winXray 的测速功能存在误解，简单的测速不但不必要的消耗流量而且没有什么实用价值，例如Ping通了TCP访问不了，TCP通了但浏览器被阻断，或者下载测速需要连接代理服务器以后长时间测试才能得到下载速度 - 如果有很多服务器需要频繁快速地测速这样显然是不行的。**根据我长时间的实测，我使用一堆的服务器，大多时候 winXray 都能最快的找到其中速度最快、且能正常使用的线路**。要考虑到网络速度与服务器响应速度本来就是波动的，**winXray 能在连接代理服务器以前在零点几秒以内快速地在上百个代理服务器中找出最快的服务器** - 可以节省我们大量手动测试切换的时间，如果你有一个比我们幸福的网络环境，有一堆稳定流畅看4K视频的代理服务器 - 那你确实可以关闭自动切换功能 - 手动切换即可。

![winXray](./screenshots/winXray.png)

winXray支持批量导入 vless、vmess、ss、trojan、trojan-go …… 等格式的分享链接，
也可以导入v2ray,、Shadowsocks、trojan等通用订阅链接，兼容 base 64、json …… 等不同格式的服务器配置，也可以导入 Clash proxy-provider 配置，winXray将自动转化各种不兼容的配置为统一、规范的格式。winXray 有强大的兼容性，我们甚至可以直接自 github 项目地址导入代理服务器，例如直接复制链接 [https://github.com/winXray/winXray/blob/master/sub/sample.json](./sub/sample.json) 即可一键导入 winXray。

![服务器配置](./screenshots/config.json.png)
**小技巧: JSON里点击任意字段都会显示该字段的用法说明。** 个人认为做很多对话框来配置服务器的参数是非常蠢的，winXray已经把各种代理协议的配置简化为几个统一命名的JSON字段（ **也可以作为一种标准的、统一的、通用的订阅响应格式使用** ），只要稍加学习就可以非常熟练的添加、修改各种代理协议的配置。而且对于大多数用户根本不需要改配置 - 简单的复制导入分享链接就可以，我们不必要把简单的事搞复杂。

可选在 ["/xray-core/winXray-default-servers.json"](./xray-core/winXray-default-servers.json) 文件中添加默认服务器配置（生成EXE后默认配置自动嵌入到EXE文件，可选删除该文件,也可以继续使用该文件覆盖EXE自带的默认服务器列表）。

![PAC配置](./screenshots/pac.png)
**小技巧: PAC编辑器里点击任意域名都会自动关联到单选框，可以直接切换代理或直连。**

软件首次运行时会在当前目录查找 "./xray-core/xray.exe"
发行文件仅需要 "./winXray.exe"，可选带上 "./xray-core/" 目录（ 如果没有找到会自动到v2ray官网下载，不过没有代理服务器下载有时候非常慢 )。

![端口配置](./screenshots/config.advanced.png)

<img src="./screenshots/telegram.gif" width="1031" alt="Telegram 端口配置">


text:
# winXray 小技巧

### 一、批量导入链接技巧

winXray  最新版支持直接使用 github 网址作为订阅链接。例如，我们在浏览器里访问 github项目文件的地址是这样的： [https://github.com/winXray/winXray/blob/master/sub/sample.json](./sample.json) 我们直接复制前面的链接，然后在 winXray 中点击 **「批量导入链接」** 就可以导入里面的服务器配置了。

winXray可以兼容以下格式的github文件地址( 支持省略域或并以斜杆开头的地址 ）：
https://github.com/winXray/winXray/blob/master/sub/sample.json
[/winXray/blob/master/sub/sample.json](./sample.json)
https://github.com/winXray/winXray/raw/master/sub/sample.json
[/winXray/winXray/raw/master/sub/sample.json](./sample.json)
https://raw.githubusercontent.com/winXray/winXray/master/sub/sample.json

即使在国内打不开 githubusercontent.com，winXray 仍然可以机智地调用 github 的API 顺利地把目标文件下载下来。

winXray 的订阅链接可以返回所有 winXray 能兼容的服务器配置，例如：
1、直接返回一行或多行分享链接，支持vmess,vless,ss,trojan,trojan-go 这一堆的各种通用分享格式，每行一个有效链接，无效的行 winXray 会自动忽略。
2、可以每行一个服务器配置JSON，也可以把多个服务器配置JSON放到一个数组里（就是放到方括号里），winXray都能兼容，JSON推荐使用 winXray 的语法 - winXray已经把各种不同的代理协议配置规范化为了几个简单且通用的字段。当然你可以返回一些 winXray 可以兼容的通用JSON，winXray 会最大可能的识别并转换各种JSON格式，例如服务器的字段名你可以写为add,address,server 等等不同的名字。
服务器可以返回base64编码的配置，也可以直接返回服务器配置，winXray都能识别。 winXray可以导入v2ray,、Shadowsocks、trojan等通用订阅链接，也可以导入 Clash proxy-provider 配置，winXray将自动转化各种不兼容的配置为统一、规范的格式。

我们平时复制配置和链接导入 winXray 一样，winXray都会最大可能的识别并兼容各种格式，并且尝试自动清除复制文本中的无效内容。例如使用 winXray 自带的 v2ray agent 安装服务端以后，我们不需要在给出的配置中挑选出JSON慢慢的修改和设置，你只需要把安装程序返回的一大堆账号配置文本直接复制出来（包含各种无效的、无关的说明文字）， winXray 会自动分析识别并导入有效的服务器配置。

有些人崇尚复杂，但是winXray 的目标就是让一切变得更简单，软件能自动做的事就自动做好，**人生苦短，哪有那么多时间折腾？！**

###  二、使用JSON配置服务器技巧
![服务器配置](./../screenshots/config.json.png)
JSON配置界面里点击任意字段都会显示该字段的用法说明。个人认为做很多对话框来配置服务器的参数是非常蠢的，winXray已经把各种代理协议的配置简化为几个统一命名的JSON字段（ **也可以作为一种标准的、统一的、通用的订阅响应格式使用** ），只要稍加学习就可以非常熟练的添加、修改各种代理协议的配置。而且对于大多数用户根本不需要改配置 - 简单的复制导入分享链接就可以，我们不必要把简单的事搞复杂。

###  三、Telegram 代理设置
因为Telegram 使用的IP非常多，PAC代理确实不好支持Telegram ，但是使用IP路由去适配Telegram其实是不必要的，试想一下一个IP路由的数据就几十MB - 有这个必要吗？！我们为什么不直接告诉 Telegram 他要使用的代理服务器地址，而是 他在每个IP请求的时候都去判断一下呢？。

所以更简洁、更聪明的方法就是直接告诉 Telegram 他要使用的代理服务器地址，这个操作非常简单 - 绝对比下载 Telegram 更简单，请看下面的动画：

![Telegram 代理设置](./../screenshots/telegram.gif)


text:
//style 外观样式

namespace style{
	primaryButton = {
		background={
			default=0xFF8FB2B0;
			hover=0xFF928BB3;
			disabled=0xFFCCCCCC;
		}
	};
	button = {
		background={
			default=0x668FB2B0;
			hover=0xFF928BB3;
			disabled=0xFFCCCCCC;
		}
	};
	transButton = {
		background={
			default=0;
			hover=0xFF928BB3;
			disabled=0xFFCCCCCC;
		}
	};
	checkBox = {
		color = {
			hover = 0xFFFF0000;
			active = 0xFF00FF00;
			disabled = 0xEE666666;
		}
		checked = {
			color = {
				hover = 0xFFFF0000;
				active = 0xFF00FF00;
				disabled = 0xEE666666;
			}
			iconText = '\uF14a'/*_FA_CHECK_SQUARE*/
		}
	};
	radio ={
		group = "default";
		color = {
			hover = 0xFFFF0000;
			active = 0xFF00FF00;
		}
		checked = {
			iconText = '\uF058'/*_FA_CHECK_CIRCLE*/
		}
	};
	link = {
		color = {
			default = 0xFF000080;
			hover = 0xFFFF0000;
			active = 0xFF00FF00;
		}
	};
	plainButton = {
		color = {
			default = 0xFF3C3C3C;
			hover = 0xFFFF0000;
			active = 0xFF00FF00;
			disabled = 0xFFCCCCCC;
		}
	};
	key = {
		foreground={
			default = 0x00FFFFFF;
			hover= 0xFF8ADBAF;
		};
	};
	dropdown = {
		background={
			default=0xFF68CC95;
			disabled=0xFFC4CCC8;
			hover=0xFF4A522F;
		};
		color={
			default=0xFF000000;
			disabled=0xFF8A8A8A;
			hover=0xFFFFFFFF
		};
		checked = {
			foreground={
				default = 0xFFDB8A8E;
				hover= 0xFF8ADBAF;
			};
		}
	};
	edit = {
		border={
			default={bottom=1;color=0xFF808080};
			disabled={bottom=1;color=0xFFCCCCCC}
		}
	};
	trackbar = {
		background={
			default=0xFF23ABD9
		};
		foreground={
			default=0xFFFF771C;
			hover=0xFFFF6600
		};
		color={
			default=0xFFFF5C00;
			hover=0xFFFF6600
		}
	}
}


text:
//dnsApi 域名解析
namespace dnsApi

_dll = ..raw.loadDll("dnsapi.dll");
flush = function(domain){
	return domain ? _dll.DnsFlushResolverCacheB() : _dll.DnsFlushResolverCacheEntry_A(domain);
}

/**intellisense(dnsApi)
flush(.(domain) = 清空指定域名的DNS缓存，\n如果参数不指定域名则清空所有DNS缓存
end intellisense**/


text:
//config 配置文件
import fsys.config;
config  = fsys.config( io.appData("/winXray/") );

namespace config {
	__appName = "winXray";
	__loadDefaultOutbounds = function(){
		import xray.outbounds;
		var serverData =  ..string.load("/xray-core/winXray-default-servers.json")
			: $"/xray-core/winXray-default-servers.json"

		proxy.outbounds = ..xray.outbounds.importFromString(..string.removeBom(serverData));
		..publish("outbounds.updateConfigJson",)
	}

	if(!proxy.outbounds){
		__loadDefaultOutbounds();
	}
}

if(!config.proxy.mode) config.proxy.mode = "pac";
if(!config.proxy.pacPort) config.proxy.pacPort = 0;
if(config.proxy.useHttpGlobal===null){
	config.proxy.useHttpGlobal = !_WIN10_LATER;
}

if(config.proxy.enableGitConfigGithub===null){
	config.proxy.enableGitConfigGithub = true;
}

if(config.proxy.test === null){
	config.proxy.test = true;
}

if(config.proxy.autoRefreshSubscription === null){
	config.proxy.autoRefreshSubscription = true;
}

if(!config.proxy.testInterval){
	config.proxy.testInterval = 15;
}

if(!config.proxy.maxTestServers){
	config.proxy.maxTestServers = 100;
}

if(!config.proxy.subscribeUrls){
	config.proxy.subscribeUrls = {
		fields =  {"";"checked";"ps";"url";}
	}
}

config.__resetDefaultCore = function(){
	config.core.default =  {
		inbounds={
			{
				listen="127.0.0.1";
				port=0;
				protocol="socks";
				settings={
					auth="noauth";
					udp=true;
				};
				sniffing={
					destOverride={
						"http";
						"tls"
					};
					enabled=true
				};
				tag="proxy";
			};
			{
				listen="127.0.0.1";
				port=0;
				protocol="http";
				settings={};
				sniffing={
					destOverride={
						"http";
						"tls"
					};
					enabled=true
				};
				tag="http_proxy"
			}
		};
		log={
			access="";
			error="";
			loglevel="warning"
		};
		outbounds={
			{
				tag="proxy"
			};
			{
				protocol="freedom";
				settings={};
				tag="direct";
			};
			{
				protocol="blackhole";
				settings={
					response={
						type="http"
					};
				};
				tag="block";
			}
		};
		routing={
			domainStrategy="IPIfNonMatch";
			rules={
				{
					inboundTag={
						"api"
					};
					outboundTag="api";
					type="field";
				}
			}
		};
	}
}

if(!config.core.default){
	config.__resetDefaultCore();
}

if(!config.core.default.inbounds[2]){
	config.core.default.inbounds[2]	= {
		listen="127.0.0.1";
		port=0;
		protocol="http";
		settings={};
		sniffing={
			destOverride={
				"http";
				"tls"
			};
			enabled=true
		};
		tag="http_proxy"
	}
}

/**intellisense(config)
__appName = 应用程序名
? = 配置文件名,\n读写配置并序列化为一个表对象,\n表的成员值可以是支持序列化的普通变量,支持table对象\n配置文件在首次使用时自动加载,退出程序时自动保存\n!fsys_table.
end intellisense**/


text:
//sysProxy 系统代理
import config;
import inet.conn;
import xray.github;
import xray.pacServer;

namespace sysProxy;

switch = function(mode){
	..config.proxy.mode = mode : "direct";
	..config.proxy.save();

	if(mode=="pac"){
		var pacUrl = ..xray.pacServer.getUrl();
		if(pacUrl){
			..inet.conn.setProxyAutoConfig(,pacUrl );
		}
		else {
			..xray.pacServer.restart();
		}
		..xray.github.setProxy(true);
	}
	elseif(mode=="proxy"){
		import xray.core;
		var address = xray.core.getHttpProxyAddress();

		if(address){
			..inet.conn.setProxy(,address);
			..xray.github.setProxy(true);
		}
	}
	else {
		..inet.conn.setProxy();
		..xray.github.setProxy(false);
	}
	..publish("sysProxy.modeChanged",mode)
}

switchToGlobalMode = function(){
	switch("proxy")
}

switchToPacMode = function(){
	switch("pac")
}

switchToDirectMode  = function(){
	switch("direct")
}

reset = function(proxy){
	if(proxy===false){
		if(..config.proxy.mode != "direct"){
			..inet.conn.setProxy();
			..xray.github.setProxy(false);
		}
		return;
	}

	..inet.conn.setProxy();
	switch(..config.proxy.mode);
}

..subscribe("pacServer.restarted",function(pacPort){
	if(..config.proxy.mode == "pac"){
		switchToPacMode();
	}
} )

..subscribe("xrayCore.restarted",function(socksProxyPort,httpProxPort){
	if(..config.proxy.mode != "direct"){
		reset(true);
	}
} )

/**intellisense(sysProxy)
switch(.(mode) = 切换代理模式，同步修改配置文件。\n可选参数："proxy"，"pac"，"direct"
switchToGlobalMode() = 设为全局代理，同步修改配置文件。
switchToPacMode() = 设为PAC自动代理，同步修改配置文件。
switchToDirectMode() = 设为禁用代理，同步修改配置文件。
reset(.(是否启用代理)) = 重置系统代理设置，不修改配置文件。\n省略参数时默认值为true。
end intellisense**/


text:
//outbounds 出站代理
import web.json;
import inet.url;
import win.clip;
import crypt;

namespace xray.outbounds;

exportSharedLinks = function(outbounds){
	var outString = {};
	for i,outbound in ..table.eachIndex(outbounds){
		if(outbound.protocol == "vmess"){
			var json = ..web.json.stringify({
				v = 2;
				add = outbound.address;
				aid = outbound.alterId;
				id = outbound.id;
				port = outbound.port;
				net = outbound.network;
				type = outbound.type;
				path = outbound.path;
				host = outbound.host;
				tls = outbound.tls;
				ps = outbound.ps;
			})

			var vmess = "vmess://" + ..crypt.encodeBin(json);
			..table.push(outString,vmess);
		}
		elseif(outbound.protocol == "shadowsocks"){
			var ss = "ss://" + ..crypt.encodeBin( outbound.security + ":" + outbound.id + "@" + outbound.address + ":" + outbound.port) + "#" + outbound.address + ":" + outbound.port;
			..table.push(outString,ss);
		}
		elseif(outbound.protocol == "trojan"  || outbound.protocol == "trojan-go"){
			var trojan = outbound.protocol + "://" + outbound.id + "@" + outbound.address + ":" + outbound.port;

			var info = {};
			if(outbound.network!="tcp") info.type = outbound.network;
			if(outbound.sni){ info.sni = outbound.sni;}
			info.host = outbound.host;
			info.path = outbound.path;

			if(..table.count(info)){
				trojan = ..inet.url.appendExtraInfo(trojan,info)
			}

			if(outbound.ps){
				trojan = trojan + "#" + ..inet.url.encode(outbound.ps);
			}
			..table.push(outString,trojan);
		}
		elseif(outbound.protocol == "vless"){
			var vless = "vless://" + outbound.id + "@" + outbound.address + ":" + outbound.port;

			var info = {};
			if(outbound.network!="tcp") info.type = outbound.network ;
			if(outbound.security!="none") info.encryption = outbound.security;
			if(outbound.tls) info.security = outbound.tls;
			if(outbound.flow) info.flow = outbound.flow;
			if(outbound.allowInsecure) info.allowInsecure = outbound.allowInsecure;
			if(outbound.alpn) info.alpn = outbound.alpn;
			if(outbound.disableSessionResumption) info.disableSessionResumption = outbound.disableSessionResumption;
			if(outbound.sni){ info.sni = outbound.sni;}
			info.host = outbound.host;
			info.path = outbound.path;
			info.seed = info.seed;
			info.key = info.key;
			info.headerType = info.type;

			if(..table.count(info)){
				vless = ..inet.url.appendExtraInfo(vless,info)
			}

			if(outbound.ps){
				vless = vless + "#" + ..inet.url.encode(outbound.ps);
			}
			..table.push(outString,vless);
		}
	}

	return ..string.join(outString,'\r\n');
}

var _subscriptionResponseCache = {
	@{_weak="kv"}
}
importFromString = function(str,subscribeUrl){
	if(!str){ return; }
	str = ..string.trim(str);

	var jsonData;
	if(..string.match(str,"%\[\]") || (
		..string.match(str,"%\{\}") && !..string.match(str,"%\{\}.+%\{\}")
		) ){

		if(subscribeUrl){
			var crc32 = ..string.crc32(str);
			_subscriptionResponseCache[subscribeUrl] = crc32;
		}
		jsonData = ..web.json.tryParse(str);
	}

	if( ..table.isArray(jsonData) ){
		for(i=#jsonData;1;-1){
			if( !validOutbound(jsonData[i]) ) {
				..table.remove(jsonData,i);
			}

			if(subscribeUrl)jsonData[i].subscribeUrl = subscribeUrl;
		}

		return jsonData;
	}
	elseif( type(jsonData) == type.table ){
		if( jsonData[["servers"]] ){

			var servers = jsonData[["servers"]]
			var outbounds = ..table.array();
			for i,v in ..table.eachIndex(servers){
				var outbound = ..table.assign(,v);
				if(outbound.protocol === null){
					outbound.protocol = (outbound.server_port&&outbound.password) ? "shadowsocks":"vless"
				}

				if(outbound.remarks){
					outbound.ps = outbound.remarks;
					outbound.remarks = null;
				}

				if(outbound.server){
					outbound.address = outbound.server;
					outbound.server = null;
				}

				if(outbound.server_port){
					outbound.port = outbound.server_port;
					outbound.server_port = null;
				}

				if(outbound.password){
					if(outbound.id){
						outbound.serverId = outbound.id;
					}
					outbound.id = outbound.password;
					outbound.password = null;
				}

				if(outbound.method){
					outbound.security = outbound.method;
					outbound.method = null;
				}

				if(subscribeUrl)outbound.subscribeUrl = subscribeUrl;
				..table.push(outbounds,outbound);
			}

			return outbounds;
		}
		else {
			if( validOutbound(jsonData) ) {
				if(subscribeUrl)jsonData.subscribeUrl = subscribeUrl;
				return {jsonData};
			}
		}
	}

	if( ..string.startWith(str,"http://")
		|| ..string.startWith(str,"https://")
		|| ..string.match(str,"^\s*/\N+\s*$")  ){

		import win.dlg.message;
		var msgDlg = win.dlg.message(..mainForm);

		var title = '正在获取订阅源 ... ';
		var cfgSubscribeUrls = ..config.proxy.subscribeUrls;
		for(i=#cfgSubscribeUrls;1;-1){
			var sub = cfgSubscribeUrls[i]
			if( (sub.url === str) ){
				if(#sub.ps) title = '正在获取订阅源: ' + sub.ps;
				..config.proxy.save();
			}
		}

		var form = msgDlg.create(title,,true)
		form.progress.startProgress(50);

		subscribeUrl = str;
		if(..string.match(subscribeUrl,"^\s*/\N+\s*$") ){
			if(!..string.find(subscribeUrl,"/<blob>|<raw>")){
				subscribeUrl = ..inet.url.append("https://raw.githubusercontent.com",subscribeUrl);
			}
			else {
				subscribeUrl = ..inet.url.append("https://github.com",subscribeUrl);
			}
		}

		str = ..win.invoke(
			function(subscribeUrl){
				import inet.http;
				import web.rest.github;
				if( string.indexOf(subscribeUrl,"github.com")
					|| string.indexOf(subscribeUrl,"raw.githubusercontent.com")
					){

					var ret  = web.rest.github.getContent(subscribeUrl)
					if(ret) return ret;
				}

				return inet.http().get(subscribeUrl);
			},subscribeUrl
		)

		form.close();
		if(!str){
			return;
		}

		var existIndex;
		var cfgSubscribeUrls = ..config.proxy.subscribeUrls : ..table.array();
		for(i=#cfgSubscribeUrls;1;-1){
			var sub = cfgSubscribeUrls[i]
			if(sub.url==subscribeUrl){
				sub.checked="启用";
				existIndex=i;
				break;
			}
		}
		if(!existIndex){
			var tUrl = ..inet.url.split(subscribeUrl);
			..table.push(cfgSubscribeUrls,{url=subscribeUrl;ps=tUrl ? tUrl.host : "";checked="启用"})
			..publish("uiCommand.subscriptionNew",cfgSubscribeUrls[#cfgSubscribeUrls]);
		}
		else{
			..publish("uiCommand.subscriptionNew",cfgSubscribeUrls[existIndex],existIndex);
		}
		..config.proxy.subscribeUrls = cfgSubscribeUrls;
		..config.proxy.save();

		var jsonData;
		if(..string.match(str,"%\[\]") || ..string.match(str,"%\{\}")){
			jsonData = ..web.json.tryParse(str);
		}

		if( type(jsonData) === type.table ){
			return importFromString(str,subscribeUrl);
		}
	}

	if(subscribeUrl){
		var crc32 = ..string.crc32(str);
		_subscriptionResponseCache[subscribeUrl] = crc32;
	}

	if(..string.match(str,"^[\w=+/-_]+$")){
		str = ..crypt.decodeBin(str);
		if(!str) return;
	}

	var outbounds = ..table.array();
	for(line in ..string.lines(str) ){
		if(#outbounds>=1500){
			import win.dlg.message;
			var msgDlg = win.dlg.message(..mainForm);
			var form = msgDlg.warn("导入的节点数目过多，已导入1500个节点并中止操作！")

			return outbounds;
		}

		if(line[1]=='/'# && line[2]=='/'#){
			continue;
		}

		var vmess2 = ..string.match(line,`vmess\:\/\/[^\@\s]+\@[^\?\#\s\:]+\:\d+\S*`);
		if( vmess2 ) {
			var vmessUrl = ..inet.url.split(vmess2)
			if(vmessUrl.user && vmessUrl.host && vmessUrl.port){
				var outbound = {
					address = vmessUrl.host;
					port = tonumber(vmessUrl.port) : 443;
					id = vmessUrl.user;
					network = "tcp";
					protocol = "vmess";
					tls = "tls";
				};

				if(vmessUrl.extraInfo){
					var info = ..inet.url.splitParameters(vmessUrl.extraInfo);
					outbound.sni = info.sni;
					outbound.host = info.host;
					outbound.path = info.path;
					outbound.security = info.encryption;
					outbound.seed = info.seed;
					outbound.key = info.key;

					outbound.network = info.type;
					outbound.type = info.headerType; //kcp || quic

					if(info.security) outbound.tls = info.security;
					if(info.flow) outbound.flow = info.flow;
					if(info.allowInsecure!==null) outbound.allowInsecure = info.allowInsecure;
					if(info.disableSessionResumption) outbound.disableSessionResumption = info.disableSessionResumption;
					if(info.alpn) outbound.alpn = info.alpn;
				}

				if(vmessUrl.location){
					outbound.ps = ..inet.url.decode(vmessUrl.location);
				}

				outbound.subscribeUrl = subscribeUrl;
				..table.push(outbounds,outbound);
			}

			continue;
		}

		var vmess = ..string.match(line,`vmess\:\/\/([\w=+]+)`);
		if( vmess ) {
			var json = ..crypt.decodeBin(vmess);
			var outbound = ..web.json.tryParse(json);
			if(type(outbound)=="table"){
				..table.clear(outbound@._defined);
				outbound.protocol = "vmess";
				outbound.address = outbound.add;
				outbound.alterId = tonumber(outbound.aid);
				outbound.security = "auto";
				outbound.network = outbound.net : "tcp";
				outbound.net = null;
				outbound.add = null;
				outbound.aid = null;
				outbound.v = null;
				outbound.subscribeUrl = subscribeUrl;

				if(outbound.ps){
					var u,h,p = ..string.match(outbound.ps,"(.+)\@(.+)\:(\d+)")
					if(u&&h&&p){
						if(h && ..string.endWith(h,"jamjams.net") && subscribeUrl && ..string.match(subscribeUrl,"justmysocks\d+\.net")){
							if(..string.match(outbound.address,"^\d+\.\d+\.\d+\.\d+$") ){
								outbound.address = h;
							}
						}
						outbound.ps = h;
					}
				}
				..table.push(outbounds,outbound);
			}

			continue;
		}

		var vless = ..string.match(line,`vless\:\/\/[^\@\s]+\@[^\?\#\s\:]+\:\d+\S*`);
		if( vless ) {
			var vlessUrl = ..inet.url.split(vless)
			if(vlessUrl.user && vlessUrl.host && vlessUrl.port){
				var outbound = {
					address = vlessUrl.host;
					port = tonumber(vlessUrl.port) : 443;
					id = vlessUrl.user;
					network = "tcp";
					protocol = "vless";
					tls = "tls";
				};

				if(vlessUrl.extraInfo){
					var info = ..inet.url.splitParameters(vlessUrl.extraInfo);
					outbound.sni = info.sni;
					outbound.host = info.host;
					outbound.path = info.path;
					outbound.security = info.encryption;
					outbound.seed = info.seed;
					outbound.key = info.key;

					outbound.network = info.type;
					outbound.type = info.headerType; //kcp || quic

					if(info.security) outbound.tls = info.security;
					if(info.flow) outbound.flow = info.flow;
					if(info.allowInsecure!==null) outbound.allowInsecure = info.allowInsecure;
					if(info.disableSessionResumption) outbound.disableSessionResumption = info.disableSessionResumption;
					if(info.alpn) outbound.alpn = info.alpn;
				}

				if(vlessUrl.location){
					outbound.ps = ..inet.url.decode(vlessUrl.location);
				}

				outbound.subscribeUrl = subscribeUrl;
				..table.push(outbounds,outbound);
			}

			continue;
		}

		var ss,comment = ..string.match(line,`ss\:\/\/([^\s\#]+)(<#\S+>?)`);
		if( ss ) {
			var userinfo,address,port = ..string.match(ss,"^([^:]+)\@(.+)\:(\d+)")
			if(!(userinfo&&address&&port)){
				var str = ..crypt.decodeBin(ss);
				if(str){ ss = str; }

				method,password,address,port = ..string.match(ss,"^(.+)\:(.+)\@(.+)\:(\d+)")
			}
			else {
				userinfo = ..crypt.decodeBin(userinfo);
				if(userinfo){
					method,password = ..string.match(userinfo,"^(.+)\:(.+)$")
				}
			}

			if(address && password && port){
				var outbound = {
					address = address;
					port = tonumber(port);
					security = method;
					id = password;
					network = "tcp";
					protocol = "shadowsocks"
				};

				if(comment){
					comment = ..string.right(comment,-2);
					comment = ..inet.url.decode(comment);
					var u,h,p = ..string.match(comment,"(.+)\@(.+)\:(\d+)")
					if(u&&h&&p){
						if(h && ..string.endWith(h,"jamjams.net") && subscribeUrl && ..string.match(subscribeUrl,"justmysocks\d+\.net")){
							if(..string.match(outbound.address,"^\d+\.\d+\.\d+\.\d+$") ){
								outbound.address = h;
							}
						}

						comment = h;
					}

					outbound.ps = comment;
				}

				outbound.subscribeUrl = subscribeUrl;
				..table.push(outbounds,outbound);
			}

			continue;
		}

		var trojan = ..string.match(line,`trojan\:\/\/[^\@\s]+\@[^\?\#\s\:]+\:\d+\S*`);
		if( trojan ) {
			var trojanUrl = ..inet.url.split(trojan)
			if(trojanUrl.user && trojanUrl.host && trojanUrl.port){
				var outbound = {
					address = trojanUrl.host;
					port = tonumber(trojanUrl.port) : 443;
					id = trojanUrl.user;
					network = "tcp";
					protocol = "trojan"
				};

				if(trojanUrl.extraInfo){
					var info = ..inet.url.splitParameters(trojanUrl.extraInfo);
					outbound.sni = info.sni || info.peer;

					outbound.host = info.host;
					outbound.path = info.path;

					if(info.type && info.type!=="original"){
						outbound.network = info.type;
						if(outbound.network=="h2"){
							outbound.network="http"
						}
					}

					outbound.security = info.encryption;
				}

				if(trojanUrl.location){
					outbound.ps = ..inet.url.decode(trojanUrl.location);
				}

				outbound.subscribeUrl = subscribeUrl;
				..table.push(outbounds,outbound);
			}

			continue;
		}

		var trojan = ..string.match(line,`trojan-go\:\/\/[^\@\s]+\@[^\?\#\s\:]+\:\d+\S*`);
		if( trojan ) {
			var trojanUrl = ..inet.url.split(trojan)
			if(trojanUrl.user && trojanUrl.host && trojanUrl.port){
				var outbound = {
					address = trojanUrl.host;
					port = tonumber(trojanUrl.port) : 443;
					id = trojanUrl.user;
					network = "tcp";
					protocol = "trojan-go"
				};

				if(trojanUrl.extraInfo){
					var info = ..inet.url.splitParameters(trojanUrl.extraInfo);
					outbound.sni = info.sni || info.peer;
					outbound.host = info.host;
					outbound.path = info.path;

					if(info.type && info.type!=="original"){
						outbound.network = info.type;
						if(outbound.network=="h2"){
							outbound.network="http"
						}
					}

					outbound.security = info.encryption;
				}

				if(trojanUrl.location){
					outbound.ps = ..inet.url.decode(trojanUrl.location);
				}

				outbound.subscribeUrl = subscribeUrl;
				..table.push(outbounds,outbound);
			}

			continue;
		}

		var clashJson = ..string.match(line,`^\s*-\s*(%\{\})\s*$`);
		if( clashJson ) {
			var outbound  = ..web.json.tryParse(clashJson)
			if(type(outbound)=="table"){
				if(outbound.type){
					if(outbound.type=="ssr"){
						continue;
					}

					if(outbound.type=="ss"){
						outbound.type =	"shadowsocks";
						outbound.network = "tcp";
					}
					outbound.protocol = outbound.type;
					outbound.type = null;
				}

				if( validOutbound(outbound) ){
					outbound.subscribeUrl = subscribeUrl;
					..table.push(outbounds,outbound );
				}
				continue;
			}
		}

		var json = ..string.match(line,`^\s*%\{\}\s*$`);
		if( json ) {
			var outbound  = ..web.json.tryParse(json)
			if(type(outbound)=="table"){
				if( validOutbound(outbound) ) ..table.push(outbounds,outbound );
				continue;
			}
		}
	}

	return outbounds;
}

importFromClipboard  = function(){
	var str = ..win.clip.read();
	if(!str){ return; }

	str = ..string.trim(str,'"\'\t\r\n ');
	return importFromString(str);
}

validAll = function(outbounds){
	for(i=#outbounds;1;-1){
		if(!validOutbound(outbounds[i])){
			..table.remove(outbounds,i);
		}
	}

	return outbounds;
}

validOutbound = function(outbound){
	if(type(outbound)!="table"){ return;  }

	if(outbound.aid){
		outbound.alterId = outbound.aid;
		outbound.aid = null;
	}

	if(outbound.add){
		outbound.address = outbound.add;
		outbound.add = null;
		..table.define(outbound);
	}

	if(outbound.net){
		outbound.network = outbound.net;
		outbound.net = null;
		..table.define(outbound);
	}

	if(outbound.peer ){
		if(#outbound.peer) outbound.sni = outbound.peer;
		outbound.peer = null;
		..table.define(outbound);
	}

	if(!outbound.security){
		if(outbound.method && outbound.method!="none"){
			outbound.security = outbound.method;
		}
		if(outbound.cipher && outbound.cipher!="none" && outbound.cipher!="auto"){
			outbound.security = outbound.cipher;
		}
		outbound.cipher = null;
		outbound.method = null;
	}

	if(!outbound.address){
		if(outbound.server){
			outbound.address = outbound.server;
			outbound.server = null;
		}
	}

	if(!outbound.ps){
		if(outbound.remarks){
			outbound.ps = outbound.remarks;
			outbound.remarks = null;
		}
		elseif(outbound.name){
			outbound.ps = outbound.name;
			outbound.name = null;
		}
	}

	if(!outbound.port){
		if(outbound.server_port){
			outbound.port = outbound.server_port;
			outbound.server_port = null;

			if(!outbound.protocol && outbound.password){
				outbound.protocol = "shadowsocks";
			}
		}
		else {
			outbound.port=443;
		}
	}

	if(!outbound.protocol){
		outbound.protocol = "vmess";
		if(outbound.ps && (..string.find(outbound.ps,"@@vless") || ..string.find(outbound.ps,"@@xtls"))){
			outbound.protocol = "vless";
		}
	}

	if(!outbound.id){
		if(outbound.password){
			outbound.id = outbound.password;
			outbound.password = null;
		}
		if(outbound.uuid){
			outbound.id = outbound.uuid;
			outbound.uuid = null;
		}
	}

	if(outbound["skip-cert-verify"]){
		outbound.allowInsecure = outbound["skip-cert-verify"];
		outbound["skip-cert-verify"] = null;
	}

	if(outbound["ws-path"]){
		outbound.path = outbound["ws-path"];
		outbound["ws-path"] = null;
	}

	if(outbound["ws-headers"]){
		outbound.headers = outbound["ws-headers"];
		outbound["ws-headers"] = null;
	}

	if(type(outbound.tls)!="string"){
		if(outbound.tls) outbound.tls = "tls";
		else outbound.tls = "";
	}

	if(outbound.servername){
		outbound.host = outbound.servername;
		outbound.servername = null;
	}

	var httpOpts = outbound["http-opts"];
	if(httpOpts){
		if(!..table.count(httpOpts)){
			httpOpts = outbound["h2-opts"] : httpOpts;
		}

		if(httpOpts.method){
			outbound.httpMethod = httpOpts.method;
		}

		if(httpOpts.path){
			outbound.path = httpOpts.path;
		}

		if(httpOpts.headers){
			outbound.headers = httpOpts.headers;
		}

		if(httpOpts.host){
			outbound.host = httpOpts.host;
		}
	}

	if(outbound.headers){
		var headers = {};
		for k,v in ..table.eachName(outbound.headers){
			var k = ..string.replace("HOST","(\a)(\a+)",lambda(a,b) ..string.upper(a) + ..string.lower(b) );
			headers[k] = v;
		}
		outbound.headers = headers;

		if(outbound.headers.Host){
			if(outbound.headers.Host==outbound.host){
				outbound.headers.Host = null;
			}
			elseif(!outbound.host){
				outbound.host = outbound.headers.Host;
				outbound.headers.Host = null;
			}
		}

		if(!..table.count(outbound.headers)){
			outbound.headers = null;
		}
	}

	outbound["http-opts"] = null;
	outbound["h2-opts"] = null;
	outbound.country = null;
	outbound.v = null;

	..table.define(outbound);

	if(outbound.address){
		return outbound;
	}
}

updateSubscription = function(outbondSubscribeUrlMap){

	var cfgOutbounds = ..config.proxy.outbounds : ..table.array();
	if(!outbondSubscribeUrlMap) {
		outbondSubscribeUrlMap = {}

		for(i=#cfgOutbounds;1;-1){
			var outbound = cfgOutbounds[i]
			if(outbound.subscribeUrl){
				outbondSubscribeUrlMap[outbound.subscribeUrl] = true;
			}
		}

		var cfgSubscribeUrls = ..config.proxy.subscribeUrls;
		for(i=#cfgSubscribeUrls;1;-1){
			var sub = cfgSubscribeUrls[i]
			if(sub.checked ===  "启用") {
				outbondSubscribeUrlMap[sub.url] = true;
			}
			else {
				outbondSubscribeUrlMap[sub.url] = null;
			}
		}

		if(!..table.count(outbondSubscribeUrlMap)){
			..mainForm.msgWarn('当前未启用任何订阅源,\n请先复制订阅源网址到剪贴板然后点击「批量导入链接」!');
			return;
		}
	}

	var count = 0;
	for(url,data in outbondSubscribeUrlMap){
		var impOutbounds = ..xray.outbounds.importFromString(url);
		if(#impOutbounds){
			for(i=#cfgOutbounds;1;-1){
				var outbound = cfgOutbounds[i]
				if(outbound.subscribeUrl==url){
					..table.remove(	cfgOutbounds,i );
				}
			}

			..table.append(cfgOutbounds,impOutbounds);
			count = count + #impOutbounds;
		}
	}

	..config.proxy.save();
	..publish("outbounds.updateConfigJson");
	..publish("uiCommand.restartXrayCore",cfgOutbounds)
	..mainForm.msgOk("已刷新" + count + "个订阅服务器。",1500);

	return count;
}

autoUpdateSubscription = function(){
	var outbondSubscribeUrlMap = {}
	var cfgSubscribeUrls = ..config.proxy.subscribeUrls;
	for(i=#cfgSubscribeUrls;1;-1){
		var sub = cfgSubscribeUrls[i]
		if(!#sub.url){
			continue;
		}

		if(sub.checked ===  "启用") {
			outbondSubscribeUrlMap[sub.url] = true;
		}
	}

	if(!..table.count(outbondSubscribeUrlMap)){
		return;
	}

	..thread.invoke(
		function(winform,outbondSubscribeUrlMap,responseCache){
			import inet.http;
			import web.rest.github;

			var http = inet.http();
			for(url,v in outbondSubscribeUrlMap){

				if( string.indexOf(subscribeUrl,"github.com")
					|| string.indexOf(subscribeUrl,"raw.githubusercontent.com")
					){
					outbondSubscribeUrlMap[url] = web.rest.github.getContent(url)
				}
				else{
					outbondSubscribeUrlMap[url] = http.get(url);
				}

				if(!outbondSubscribeUrlMap[url]){
					continue;
				}

				if(responseCache){
					if(responseCache[url] && responseCache[url]===..string.crc32(outbondSubscribeUrlMap[url])){
						outbondSubscribeUrlMap[url] = null;
					}
				}
			}

			winform.publish("thread.subscriptionUpdated",outbondSubscribeUrlMap)
		},..mainForm,outbondSubscribeUrlMap,_subscriptionResponseCache
	)
}

..subscribe("thread.subscriptionUpdated",function(outbondSubscribeUrlMap){
	var cfgOutbounds = ..config.proxy.outbounds : ..table.array();

	var count = 0;
	for(url,data in outbondSubscribeUrlMap){

		var impOutbounds = ..xray.outbounds.importFromString(data,url);
		if(#impOutbounds){
			for(i=#cfgOutbounds;1;-1){
				var outbound = cfgOutbounds[i]
				if(outbound.subscribeUrl==url){
					..table.remove(	cfgOutbounds,i );
				}
			}

			..table.append(cfgOutbounds,impOutbounds);
			count = count + #impOutbounds;
		}
	}

	if(!count){
		return;
	}

	..config.proxy.outbounds = cfgOutbounds;
	..config.proxy.save();

	..publish("outbounds.updateConfigJson");
	..publish("uiCommand.restartXrayCore")
	..publish("uiCommand.print","已自动刷新" + count + "个订阅服务器。")
} )

/**intellisense(xray.outbounds)
exportSharedLinks(.(outbounds) = 导出分享链接
importFromString(.(str,subscribeUrl) = 导入分享链接
importFromClipboard() = 自剪贴板导入分享链接
autoUpdateSubscription() = 自动并静默更新订阅源
updateSubscription() = 更新或自剪贴板导入订阅源\n可选在参数中用一个表自定义要更新的订阅地址,\n参数表中键为url，值为true
end intellisense**/


text:
//github 代理设置
import xray.core;
import process.popen;
namespace xray.github;

setProxy = function(enableProxy){
	if(enableProxy===null){
			enableProxy = (..config.proxy.mode === "pac") || (..config.proxy.mode === "proxy")

			if(!..config.proxy.enableGitConfigGithub){
			return;
		}
	}

	var socksPort = ..xray.core.socksProxyPort;
	if(!socksPort && enableProxy) return;

	var git;
	if(enableProxy){
		git = ..process.popen("git config --global http.https://github.com.proxy socks5://127.0.0.1:" + socksPort)

		var path = ..io.getSpecial(0x28/*_CSIDL_PROFILE*/,".ssh/config")
		var str = ..string.load(path):"";
		if(..string.find(str,"Host\s+github.com" ) ){
			if(!..string.find(str,"Host\s+github.com\s+ProxyCommand\s+connect\s+\-S\s+127\.0\.0\.1\:\d+ \-a none \%h \%p" )){
				str = ..string.replace(str,"Host\s+github.com",'Host github.com\r\n    ProxyCommand connect -S 127.0.0.1:' + socksPort + ' -a none %h %p')
			}
		}
		else {
			str = str + '\r\n\r\nHost github.com\r\n    ProxyCommand connect -S 127.0.0.1:' + socksPort + ' -a none %h %p'
		}

		..string.save(path,str)
	}
	else {
		git = ..process.popen("git config --global --unset http.https://github.com.proxy")
		var path = ..io.getSpecial(0x28/*_CSIDL_PROFILE*/,".ssh/config")
		var str = ..string.load(path):"";
		if(..string.find(str,"Host\s+github.com" ) ){
			if(..string.find(str,"Host\s+github.com\s+ProxyCommand\s+connect\s+\-S\s+127\.0\.0\.1\:\d+ \-a none \%h \%p" )){
				str = ..string.replace(str,"Host\s+github.com\s+ProxyCommand\s+connect\s+\-S\s+127\.0\.0\.1\:\d+ \-a none \%h \%p",'Host github.com')
				..string.save(path,str)
			}
		}
	}
}


text:
//pacServer 自动代理服务
import config;
import wsock.tcp.simpleHttpServer;
import inet.http;
import inet.url;

namespace xray.pacServer;
$serverMain = ..wsock.tcp.simpleHttpServer.mainThread(
	function(response,request,session){

		if(request.path=="/proxy.pac"){
			var pacData = ..string.load(pacPath);
			var proxPorts = inboundPort.get();
			if(!proxPorts[["socksProxy"]]){
				response.errorStatus(404);
				return;
			}

			var reps = ..string.replace(pacData,"var\s+proxy\s+=\s+%''","var proxy = 'SOCKS5 "+ request.environ.SERVER_NAME +":"+proxPorts.socksProxy +";SOCKS "+ request.environ.SERVER_NAME +":"+proxPorts.socksProxy +"'");
			if(useHttpProxy.get()){
				reps = ..string.replace(pacData,"var\s+proxy\s+=\s+%''","var proxy = 'PROXY "+ request.environ.SERVER_NAME +":"+proxPorts.httpProxy +"'");
			}

			response.write(reps);
		}
	}
);
$serverMain.threadNum = 50;

var pacPath = ..io.appData("/winXray/pac.txt");
resetPac = function(){
	..string.save(pacPath,$"/pac.txt" )
}

if( ! ..io.exist(pacPath) ){
	resetPac();
}

loadPacText = function(){
	return ..io.exist(pacPath) ? ..string.load(pacPath) || $"/pac.txt";
}

savePacText = function(json){
	var str = ..string.replace($"/pac.txt","var\s+rules\s*=\s*%\[\]","var rules = " + json);
	..string.save(pacPath,str );
}

var inboundPort = ..thread.var();
var useHttpProxy = ..thread.var();
restart = function(){
	useHttpProxy.set(..config.proxy.useHttpGlobal);

	$serverMain.threadGlobal = {
		inboundPort = inboundPort;
		pacPath = pacPath;
		useHttpProxy = useHttpProxy;
	}

	var pacPort = ..config.proxy.pacPort;
	if( (! pacPort) || pacPort >= 49152 ){ pacPort = ..wsock.tcp.server.getFreePort("127.0.0.1",1083,10803,10813,44823) }

	var currentIp,currentPort = $serverMain.getLocalIp();
	if(currentIp!==..config.core.default.inbounds[[1]][["listen"]]
		|| currentPort != pacPort ){
		$serverMain.stop();

		var ok,err = $serverMain.start(
			..config.core.default.inbounds[[1]][["listen"]] : "127.0.0.1",pacPort );
		if(!ok){
			..publish("uiCommand.print","启动PAC服务器失败",err)
			return false,err;
		}

		..publish("uiCommand.print","PAC服务器已启动",getUrl())
		..publish("pacServer.restarted",pacPort);
	}

	return true;
}

stop = function(){
	if($serverMain){
		$serverMain.stop();
	}
}

updateUseHttpProxy = function(httpProxy){
	useHttpProxy.set(httpProxy);
}

updateConfig = function(socksProxyPort,httpProxyPort){
	if(socksProxyPort && httpProxyPort){
		inboundPort.set( {socksProxy=socksProxyPort;httpProxy=httpProxyPort} );
	}
}

..subscribe("xrayCore.restarted",function(socksProxyPort,httpProxyPort){
	updateConfig(socksProxyPort,httpProxyPort);
} )

getUrl = function(localIp){
	var url = $serverMain.getUrl("/proxy.pac?" + ..time.tick() )
	if(!url){ return }

	var tUrl = ..inet.url.split(url);
	if(!localIp){
			tUrl.host = "127.0.0.1"
		}
	else {
		tUrl.host = ..wsock.tcp.client.getLocalIp("www.baidu.com");
	}

	return tostring(tUrl);
}

getPort = function(){
	var ip,port = $serverMain.getLocalIp();
	return port;
}

/**intellisense(xray.pacServer)
stop() = 停止PAC服务器
restart() = 重启PAC服务器
getUrl(.(localIp) = 返回PAC服务地址
getPort() = 返回PAC端口
updateConfig(.(socksProxyPort,httpProxPort) = 更新代理端口或PAC数据
resetPac() = 重置为默认PAC
end intellisense**/


text:
//core 代理服务
import wsock.tcp.server;
import process.popen;
import config;

namespace xray.core;

socksProxyPort = null;
httpProxPort = null;

getHttpProxyAddress = function(){
    if(!(socksProxyPort&&httpProxPort)){ return null; }
	return ..config.proxy.useHttpGlobal ? ("127.0.0.1:" + httpProxPort ): ("SOCKS=127.0.0.1:"+socksProxyPort)
}

isInboundPortChanged = function(){
	var inbounds = ..config.core.default.inbounds;
	if(! (..table.isArray(inbounds) && inbounds[1] && inbounds[2]) ){
		return true;
	}

	return (inbounds[1].port!=socksProxyPort) || (inbounds[2].port!=httpProxPort)
}

var prcsXray;
import process.job.limitKill;
restart = function(editor,outbound){
    if(!outbound){ return null,"错误的出站代理服务器配置"  }
    if(_WINXP){
    	return null,"抱歉！Xray Core 不支持 Windows XP, 仅支持 Windows 7, Windows 10 以及更新操作系统。";
    }

	var inbounds = ..config.core.default[["inbounds"]];
	if(! (..table.isArray(inbounds) && inbounds[1] ) ){
		return null,"错误的入站SOCKS代理服务器配置"
	}
	if(! ( inbounds[2] ) ){
		return null,"错误的入站HTTP代理服务器配置"
	}

	if(prcsXray){
		prcsXray.terminate();
		prcsXray = null;
	}

	socksProxyPort = inbounds[1].port;
	httpProxPort = inbounds[2].port;
	if( (! socksProxyPort) || socksProxyPort >=  49152 ){ socksProxyPort = ..wsock.tcp.server.getFreePort("127.0.0.1",1081,10801,10811,44821)  }
	if( (! httpProxPort) || httpProxPort >=  49152 ){ httpProxPort = ..wsock.tcp.server.getFreePort("127.0.0.1",1082,10802,10812,44822) }

	var corePath = getPath(editor.hwnd);
	if(!corePath){ return false,"启动失败,未找到xray.exe"; }

	import xray.core.configJson;
	var jsonPath,err = ..xray.core.configJson.write(
		..io.joinpath(..io.splitpath(corePath).dir,"config.json")
		,outbound,socksProxyPort,httpProxPort);
	if(!jsonPath){
		return false,err:"启动失败,写入配置文件遇到错误！";
	}

	..publish("activeOutbound",false);

	import sysProxy;
	..sysProxy.reset(false);

	var err;
	prcsXray,err = ..process.popen(corePath,"-c=config.json" );
	if(!prcsXray){
		..publish("uiCommand.print",err:"启动 Xray Core 时遇到未知错误!");

		if(!..process().isWow64()){
			if(..process.isExe(corePath)!="PE32"){
				..io.remove(corePath);
				if(!..io.exist(corePath)){
					..publish("uiCommand.print","当前操作系统是32位，已删除无效的64位 Xray Core，正在下载32位 Xray Core");
					return restart(editor,outbound)
				}
				else {
					..publish("uiCommand.print","当前操作系统是32位，但是找到的 Xray Core 是64位，请重新下载32位 Xray Core");
				}
			}
		}

		return;
	}
	prcsXray.assignToJobObject(process.job.limitKill);
	prcsXray.codepage = 65001;
	prcsXray.logResponse(editor);

	inbounds[1].port = socksProxyPort;
	inbounds[2].port = httpProxPort;
	..config.core.save();

	..publish("uiCommand.print","已启动代理服务器，SOCKS端口:" + socksProxyPort + " HTTP端口:" + httpProxPort)
	..publish("xrayCore.restarted",socksProxyPort,httpProxPort);
	return true;
}

stop = function(){
	if(prcsXray){
		prcsXray.terminate();
		prcsXray = null;
	}

	..sysProxy.reset(false);

	import xray.github;
   	xray.github.setProxy(false);
}

var getXrayCoreUrl = function(){
	import inet.http;
	var http = ..inet.http();
	var url = http.location("https://github.com/XTLS/Xray-core/releases/latest");
	http.close();

	if(!url)return;

	var tag = ..string.match(url,"[^/]+$")
	if(!tag) return;

	return "https://github.com/XTLS/Xray-core/releases/download/"
		+ tag + "/xray-windows-" +	(..process().isWow64() ? "64" : "32") + ".zip",tag
}

getPath = function(hwnd){
	var path = ..io.fullpath("/xray-core/xray.exe");
	if(..io.exist(path)){
		return path;
	}

	var path = ..io.appData("/xray/core/xray.exe");
	if(..io.exist(path)){
		return path;
	}

	var url = getXrayCoreUrl();
	if(!url) return null;

	import zlib.httpFile;
	if( ..zlib.httpFile.download(url,"正在下载 Xray Core"
		,..io.appData("/xray/download/")
		,..io.appData("/xray/core/"),,hwnd) )
		return ..io.exist(path);
}

updateCore = function(){
	var coreDir = ..io.exist("/xray-core/xray.exe") ? ..io.fullpath("/xray-core/");
	if(!coreDir){
		coreDir = ..io.appData("/xray/core/");
	}

	var url,versionTag = getXrayCoreUrl();
	if(!url) return null;

	import fsys;
	fsys.delete(..io.appData("/xray/temp/"))

	import zlib.httpFile;
	if( ..zlib.httpFile.download(url,"正在下载 Xray Core"
		,..io.appData("/xray/download/")
		,..io.appData("/xray/temp/"),,..mainForm.hwnd) ){
			if(..io.exist(..io.appData("/xray/temp/xray.exe"))){
				import sysProxy;
				sysProxy.reset(false);

				import process.file;
				process.file.terminate(..io.joinpath(coreDir,"xray.exe"));
				fsys.copy(..io.appData("/xray/temp/xray.exe"),..io.joinpath(coreDir,"xray.exe"));

				return versionTag;
			}
		}
}

/**intellisense(xray.core)
socksProxyPort = SOCKS代理端口
httpProxPort = HTTP代理端口
isInboundPortChanged() = 是否已变更代理端口配置
restart(.(editor,outbound) = 重启启动 Xray 服务进程
end intellisense**/


text:
//configJson 启动配置
import config;
import web.json;
import inet.url;
import crypt;

namespace xray.core.configJson;

write  = function(jsonPath,outbound,sockPort,httpPort){
    if(!outbound){ return; }
	if(type(outbound.port)!=type.number){
		outbound.port = tonumber(outbound.port);
	}

	var current = ..table.clone(..config.core.default);
	var inbounds = current.inbounds;
	if(! (..table.isArray(inbounds) && inbounds[1] ) ){
		return null,"错误的入站SOCKS代理服务器配置"
	}

	inbounds[1].pacPort = null;
	inbounds[1].port = sockPort;
	if(inbounds[2]){ inbounds[2].port = httpPort; }

	if(#outbound.tls && !outbound.sni){
		var host = outbound.host;
		if(..table.isArray(host)){ host = host[1] }
		outbound.sni = host ? host : outbound.address
	}

	if(outbound.protocol=="vless"){
		if(!outbound.tls && outbound.port==443) outbound.tls = "tls";
		if(!outbound.tls) outbound.tls = "tls";

		var mux = (outbound.concurrency !== null) ? {
			concurrency=outbound.concurrency;
			enabled=outbound.concurrency ? ( outbound.concurrency > 0)
		} : {
			concurrency=8;
			enabled=true
		};

		var ob = {
			mux = outbound.tls != "xtls" ? mux : null;
			protocol="vless";

			settings={
				vnext={
					{
						address=outbound.address;
						port=tonumber(outbound.port);
						users={
							{
								id=outbound.id;
								flow=outbound.flow : (outbound.tls == "xtls" ? "xtls-rprx-direct" : null);
								encryption=( (!#outbound.security) || outbound.security=="auto") ? "none" : outbound.security;
								level=outbound.level : 0;
							}
						}
					}
				};
			};
			streamSettings={
				network=outbound.network : "tcp";
				security=#outbound.tls ? outbound.tls : null;
				tlsSettings = (#outbound.tls && outbound.tls != "xtls") ? {
					allowInsecure = !!outbound.allowInsecure;
					serverName = outbound.sni : outbound.address;
					alpn = outbound.alpn;
					disableSessionResumption = outbound.disableSessionResumption;
				} : null;
				xtlsSettings = outbound.tls == "xtls" ? {
					allowInsecure = !!outbound.allowInsecure;
					serverName = outbound.sni : outbound.address;
					alpn = outbound.alpn;
					disableSessionResumption = outbound.disableSessionResumption;
				} : null;
			};
			tag="proxy"
		};

		if( outbound.network == "ws" ){
			ob.streamSettings.wsSettings = {
				path = outbound.path;
				headers = outbound.headers;
			}

			if( outbound.host ) {
				if(!ob.streamSettings.wsSettings.headers){
					ob.streamSettings.wsSettings.headers = {}
				}
				ob.streamSettings.wsSettings.headers.Host = outbound.host;
			}
		}
		elseif( (outbound.network == "http") || (outbound.network == "h2")  ){
			var host = outbound.host;
			if(type.isString(host)){host = {host}};

			ob.streamSettings.httpSettings = {
				path = outbound.path;
				host = host;
			}
		}
		elseif( outbound.network == "tcp" ){
			if( outbound.type == "http" ){
				ob.streamSettings.tcpSettings = {
					header = {
						request={
							headers=..table.assign({
								Connection={
									"keep-alive"
								};
								Host=outbound.host;
								Pragma="no-cache";
								["Accept-Encoding"]={
									"gzip, deflate"
								};
								["User-Agent"]={
									"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36";
									"Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/53.0.2785.109 Mobile/14A456 Safari/601.1.46"
								}
							},outbound.headers);
							method = outbound.httpMethod : "GET";
							path = outbound.path;
							version="1.1"
						};
						type="http";
					}
				}
			}
		}
		elseif( outbound.network == "kcp" ){
			ob.streamSettings.kcpSettings = {
				congestion = outbound.congestion;
				seed = outbound.seed;
				mtu = outbound.mtu;
				tti = outbound.tti;
				uplinkCapacity = outbound.uplinkCapacity;
				downlinkCapacity = outbound.downlinkCapacity;
				readBufferSize = outbound.readBufferSize;
				writeBufferSize = outbound.writeBufferSize;
				header = {
					type = outbound.type;
				}
			}
		}
		elseif( outbound.network == "quic" ){
			ob.streamSettings.kcpSettings = {
				key = outbound.key;
				security = outbound.security ? outbound.security : "none";
				header = {
					type = outbound.type;
				}
			}
		}

		..table.assign(current.outbounds[1],ob);
	}
	elseif(outbound.protocol=="vmess"){
		if(!#outbound.security) outbound.security = "auto";

		var ob = {
			mux = (outbound.concurrency !== null) ? {
				concurrency=outbound.concurrency;
				enabled=outbound.concurrency ? ( outbound.concurrency > 0)
			} : {
				concurrency=8;
				enabled=true
			};
			protocol="vmess";
			settings={
				vnext={
					{
						address=outbound.address;
						level=outbound.level : null;
						port=tonumber(outbound.port);
						users={
							{
								alterId=tonumber(outbound.alterId) : 0;
								email=outbound.email : "t@t.tt";
								id=outbound.id;
								security=outbound.security;
							}
						}
					}
				};
			};
			streamSettings={
				network=outbound.network : "tcp";
				security=#outbound.tls ? outbound.tls : null;
				tlsSettings = #outbound.tls ? {
					allowInsecure = !!outbound.allowInsecure;
					serverName = outbound.sni;
				} : null
			};
			tag="proxy"
		};

		if( outbound.network == "ws" ){
			ob.streamSettings.wsSettings = {
				path = outbound.path;
				headers = outbound.headers;
			}

			if( outbound.host ) {
				if(!ob.streamSettings.wsSettings.headers){
					ob.streamSettings.wsSettings.headers = {}
				}
				ob.streamSettings.wsSettings.headers.Host = outbound.host;
			}
		}
		elseif( (outbound.network == "http") || (outbound.network == "h2")  ){
			var host = outbound.host;
			if(type.isString(host)){host = {host}};

			ob.streamSettings.httpSettings = {
				path = outbound.path;
				host = host;
			}
		}
		elseif( outbound.network == "tcp" ){
			if( outbound.type == "http" ){
				ob.streamSettings.tcpSettings = {
					header = {
						request={
							headers=..table.assign({
								Connection={
									"keep-alive"
								};
								Host=outbound.host;
								Pragma="no-cache";
								["Accept-Encoding"]={
									"gzip, deflate"
								};
								["User-Agent"]={
									"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36";
									"Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/53.0.2785.109 Mobile/14A456 Safari/601.1.46"
								}
							},outbound.headers);
							method=outbound.httpMethod : "GET";
							path = outbound.path;
							version="1.1"
						};
						type="http";
					}
				}
			}
		}
		elseif( outbound.network == "kcp" ){
			ob.streamSettings.kcpSettings = {
				congestion = outbound.congestion;
				seed = outbound.seed;
				mtu = outbound.mtu;
				tti = outbound.tti;
				uplinkCapacity = outbound.uplinkCapacity;
				downlinkCapacity = outbound.downlinkCapacity;
				readBufferSize = outbound.readBufferSize;
				writeBufferSize = outbound.writeBufferSize;
				header = {
					type = outbound.type;
				}
			}
		}
		elseif( outbound.network == "quic" ){
			ob.streamSettings.kcpSettings = {
				key = outbound.key;
				security = outbound.security ? outbound.security : "none";
				header = {
					type = outbound.type;
				}
			}
		}

		..table.assign(current.outbounds[1],ob);
	}
	elseif(outbound.protocol=="trojan" || outbound.protocol=="trojan-go"){
		outbound.tls = #outbound.tls ? outbound.tls : "tls";
		var ob = {
			mux = (outbound.concurrency !== null) ? {
				concurrency=outbound.concurrency;
				enabled=outbound.concurrency ? ( outbound.concurrency > 0)
			} : {
				concurrency=-1;
				enabled=false
			};
			protocol="trojan";
			settings={
				servers={
					{
						address=outbound.address;
						email=outbound.email;
						password=outbound.id;
						port=outbound.port;
						level=outbound.level : 0;
						encryption=outbound.security;
						flow=outbound.flow : (outbound.tls == "xtls" ? "xtls-rprx-direct" : null);
					}
				};
			};
			streamSettings={
				network = outbound.network : "tcp";
				security = #outbound.tls ? outbound.tls : "tls";

				tlsSettings =  (outbound.tls === "tls") ? {
					allowInsecure = !!outbound.allowInsecure;
					serverName = outbound.sni;
				} : null
			};
			tag="proxy"
		};

		if( outbound.network == "ws" ){
			ob.streamSettings.wsSettings = {
				path = outbound.path;
				headers = outbound.headers;
			}

			if( outbound.host ) {
				if(!ob.streamSettings.wsSettings.headers){
					ob.streamSettings.wsSettings.headers = {}
				}
				ob.streamSettings.wsSettings.headers.Host = outbound.host;
			}
		}
		..table.assign(current.outbounds[1],ob);
	}
	elseif(outbound.protocol=="shadowsocks"){
		var ob = {
			mux = (outbound.concurrency !== null) ? {
				concurrency=outbound.concurrency;
				enabled=outbound.concurrency ? ( outbound.concurrency > 0)
			} : {
				concurrency=-1;
				enabled=false
			};
			protocol="shadowsocks";
			settings={
				servers={
					{
						address=outbound.address;
						level=outbound.level : 0;
						method=outbound.security;
						ota=false;
						password=outbound.id;
						port=outbound.port;
					}
				};
			};
			streamSettings={
				network = outbound.network : "tcp";
			};
			tag="proxy"
		};

		..table.assign(current.outbounds[1],ob);
	}

	..string.save(jsonPath,..web.json.stringify(current) );
	return jsonPath;
}


text:
import fonts.fontAwesome;
import win.ui;
/*DSG{{*/
var winform = win.form(text="aardio form";right=912;bottom=793;bgcolor=16777215)
winform.add(
btnUpdate={cls="plus";text="更新设置";left=731;top=750;right=863;bottom=781;align="left";bgcolor=11580047;db=1;dr=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=22}};iconText='\uF0C7';notify=1;textPadding={left=40};z=7};
editCoreConfig={cls="edit";left=10;top=34;right=903;bottom=744;db=1;dl=1;dr=1;dt=1;edge=1;hide=1;hscroll=1;multiline=1;vscroll=1;z=6};
editOutbounds={cls="edit";left=9;top=32;right=902;bottom=743;db=1;dl=1;dr=1;dt=1;edge=1;hscroll=1;multiline=1;vscroll=1;z=1};
lbTip={cls="static";text="可在下面输入JSON数组、或每行一个服务器链接、或每行以空格分开的服务器配置文本：";left=11;top=9;right=899;bottom=29;color=3947580;dl=1;dr=1;dt=1;font=LOGFONT(h=-13);transparent=1;z=8};
navCoreConfig={cls="plus";text="高级配置( JSON )";left=184;top=750;right=315;bottom=782;border={top=1;color=-16744448};db=1;dl=1;z=3};
navOutbounds={cls="plus";text="服务器配置( JSON )";left=53;top=750;right=184;bottom=782;border={top=1;color=-16744448};db=1;dl=1;z=2};
navRight={cls="plus";left=314;top=750;right=604;bottom=751;bgcolor=16777215;border={color=-16744448};db=1;dl=1;dr=1;forecolor=32768;linearGradient=180;z=4};
plus={cls="plus";left=9;top=750;right=53;bottom=751;border={color=-16744448};db=1;dl=1;forecolor=32768;z=5}
)
/*}}*/

import config;
import web.json;
import win.ui.tabs;
var tbs = win.ui.tabs(winform.navOutbounds,winform.navCoreConfig,winform.nav06,winform.nav091,winform.navZhengMa,winform.navTxt)
tbs.margin = 0;

tbs.skin({
	foreground={
		active=0xFFFFFFFF;
		default=0x00FFFFFF;
		hover=0xFFCCCCCC;
	};
	checked={
		foreground={
			default=0x00FFFFFF;
		};
		border = {
			default = {left=1;right=1;bottom=1;color=0xFF008000}
		};
	}
});

tbs.onSelchange = function(idx,strip,form){
	if(idx==1){
		winform.editOutbounds.hide = false;
		winform.editCoreConfig.hide = true;
		winform.lbTip.text = `可在下面输入JSON数组、或每行一个服务器链接、或每行以空格分开的服务器配置文本：`
	}
	else {
		winform.editOutbounds.hide = true;
		winform.editCoreConfig.hide = false;
		winform.lbTip.text = `outbounds[1]会被自动替换为服务器数组，请勿修改： `
	}
}
tbs.selIndex = 1;

import style;
winform.btnUpdate.skin(style.primaryButton);
winform.btnUpdate.oncommand = function(id,event){
	winform.btnUpdate.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'};

	if(tbs.selIndex==1){
		if(..string.match(winform.editOutbounds.text,"%\[\]")){
			var cfg,err = web.json.tryParse(winform.editOutbounds.text);
			if(!cfg){
				winform.editOutbounds.showErrorTip(,string.concat("JSON语法错误 ",err));
				winform.btnUpdate.disabledText = null;
				return;
			}
			config.proxy.outbounds = cfg : ..table.array();
		}
		else {
			var outbounds = xray.outbounds.importFromString(winform.editOutbounds.text);
			config.proxy.outbounds = outbounds : ..table.array();
			winform.editOutbounds.text = web.json.stringify(outbounds,true);
		}

		config.proxy.save();
		winform.editOutbounds.modified = false;
		publish("config.proxy.outbounds.changed",cfg)
	}
	elseif(tbs.selIndex==2) {
		var cfg,err = web.json.tryParse(winform.editCoreConfig.text);
		if(type(cfg)!==type.table){
			winform.editCoreConfig.showErrorTip(,string.concat("JSON语法错误 ",err));
			winform.btnUpdate.disabledText = null;
			return;
		}

		if(cfg.outbounds[[1]][["tag"]]!="proxy"){
			winform.msgErr(`outbounds[1]必须指定为{"tag":"proxy"}`);
			winform.btnUpdate.disabledText = null;
			return;
		}

		config.core.default = cfg;
		config.core.save();
		publish("config.proxy.outbounds.changed",cfg)
	}

	win.delay(500);
	winform.btnUpdate.disabledText = null;
	winform.msgOk("配置已更新",1200);
}

import xray.outbounds;
winform.editOutbounds.enablePopMenu(function(){
	if(tbs.selIndex==1){
		var strEditBounds = string.trim(winform.editOutbounds.text);
		var menu = {
			{ /*---分隔线---*/ };
			{ "自剪贴板批量导入vmess、trojan、ss链接、或订阅源";
				function(id){

					var outbounds = xray.outbounds.importOutboundFromClip();
					if(#outbounds){
						var json = strEditBounds;
						if( (!#json) || ..string.match(json,"%\[\]")){
							var cfg,err = ..table.array();
							if(#json){
								cfg,err= web.json.tryParse(strEditBounds);
								if(!cfg){
									winform.editOutbounds.showErrorTip(,string.concat("当前配置存在JSON语法错误 ",err));
									return;
								}
							}

							..table.append(cfg,outbounds);
							winform.editOutbounds.text = ..web.json.stringify(cfg,true);
						}
						else {
							var str = xray.outbounds.exportSharedLinks(outbounds);
							if(#strEditBounds){
								winform.editOutbounds.text = strEditBounds+ '\r\n' + str;
							}
							else {
								winform.editOutbounds.text = str;
							}
						}

						winform.msgOk("已成功导入" + #outbounds + "个服务器,请点击「更新设置」启用新配置。",1200);
						winform.editOutbounds.modified = true;
						winform.editOutbounds.setFocus();
						return;
					}

					winform.msgFrown("未导入服务器, 请先复制订阅源、或vmess、ss、trojan链接！")
				}
			};
		}

		if( (!#strEditBounds) || ..string.match(strEditBounds,"%\[\]")){
			menu[3] = { "更新订阅源";
				function(id){
					var cfg,err = ..table.array();

					var json = strEditBounds;
					if(#json){
						cfg,err = web.json.tryParse(strEditBounds);
						if(!cfg){
							winform.editOutbounds.showErrorTip(,string.concat("JSON语法错误 ",err));
							return;
						}
					}

					var subscribeUrls = {}
					for(i=#cfg;1;-1){
						var outbound = cfg[i]
						if(outbound.subscribeUrl){
							subscribeUrls[outbound.subscribeUrl] = true;
						}
					}

					if(!table.count(subscribeUrls)){
						import win.clip;
						var clibStr = win.clip.read();
						if( ..string.startWith(clibStr,"http://") || ..string.startWith(clibStr,"https://")  ){
							subscribeUrls[clibStr] = true;
						}
						else {
							winform.msgWarn('当前配置不包含来自订阅源的服务器,\n请先复制订阅源网址到剪贴板!');
							return;
						}
					}

					var count = 0;
					for(url,v in subscribeUrls){
						var outbounds = xray.outbounds.importFromString(url);
						if(#outbounds){
							for(i=#cfg;1;-1){
								var outbound = cfg[i]
								if(outbound.subscribeUrl==url){
									..table.remove(	cfg,i );
								}
							}

							count = count + #outbounds;
							..table.append(cfg,outbounds);
						}
					}

					winform.editOutbounds.text = ..web.json.stringify(cfg,true);

					winform.msgOk("已成功刷新" + count + "个服务器,请点击「更新设置」启用新配置。",1200);
					winform.editOutbounds.modified = true;
					winform.editOutbounds.setFocus();
					return;
				}
			};
		}

		if(..string.match(strEditBounds,"%\[\]")){
			menu[4] = {}
			menu[5] = { "转换为 vmess、trojan、ss 分享链接";
				function(id){
					var cfg,err = web.json.tryParse(strEditBounds);
					if(!cfg){
						winform.editOutbounds.showErrorTip(,string.concat("JSON语法错误 ",err));
						return;
					}

					var str = xray.outbounds.exportSharedLinks(cfg);
					if(str){
						winform.editOutbounds.text = str;
						winform.editOutbounds.modified = true;
					}
				}
			};
			menu[6] = { "转换为 Base64 订阅源";
				function(id){
					var cfg,err = web.json.tryParse(strEditBounds);
					if(!cfg){
						winform.editOutbounds.showErrorTip(,string.concat("JSON语法错误 ",err));
						return;
					}

					var str = xray.outbounds.exportSharedLinks(cfg);
					if(str){
						import crypt;
						winform.editOutbounds.text = ..crypt.encodeBin(str);
						winform.editOutbounds.modified = true;
					}
				}
			};
			menu[7] = { "转换为文本格式";
				function(id){
					var cfg,err = web.json.tryParse(strEditBounds);
					if(!cfg){
						winform.editOutbounds.showErrorTip(,string.concat("JSON语法错误 ",err));
						return;
					}

					var outstr = {};
					for i,outbound in table.eachIndex(cfg){
						if(outbound.protocol != "shadowsocks"){
							var path = outbound.path;
							if( outbound.host){
								path = ( (#outbound.type && outbound.type!="none") ? outbound.type : outbound.network : "")
									+ "://" + outbound.host ++ path;
							}

							table.push(outstr, string.concat( outbound.address,'\t'
								,outbound.port,'\t'
								,outbound.id,,'\t'
								,outbound.network,'\t'
								,path,'\t'
								,outbound.tls ) );
						}
						else {
							table.push(outstr, string.concat( outbound.address,'\t'
								,outbound.port,'\t'
								,outbound.id,,'\t'
								,'shadowsocks\t'
								,outbound.security) );
						}
					}

					winform.editOutbounds.text = string.join(outstr,'\r\n');
					winform.editOutbounds.modified = true;
				}
			};
		}
		elseif( #strEditBounds ) {
			menu[3] = {}

			menu[4] = { "转换为 JSON";
				function(id){
					import web.json;
					var outbounds = xray.outbounds.importFromString(strEditBounds);
					if(outbounds){
						winform.editOutbounds.text = web.json.stringify(outbounds,true);
						winform.editOutbounds.modified = true;
					}
				}
			};
		}

		table.push(menu,{ /*---分隔线---*/ });
		if(winform.editOutbounds.modified){
			table.push(menu,{ "恢复为当前服务器配置(JSON)";
				function(id){
					winform.editOutbounds.text = web.json.stringify(
						config.proxy.outbounds,true
					)
					winform.editOutbounds.modified = false;
				}
			});
		}

		table.push(menu,{ "恢复为默认服务器配置(JSON)";
			function(id){
				..config.__loadDefaultOutbounds();
				publish("config.proxy.outbounds.changed",cfg);
			}
		});

		return menu;
	}
})

subscribe("config.proxy.outbounds.config.changed",function(){
	if(winform.editOutbounds.modified){
		return;
	}

	winform.editOutbounds.text = web.json.stringify(
		config.proxy.outbounds,true
	)

	winform.editOutbounds.modified = false;
} )

winform.editOutbounds.text = #config.proxy.outbounds ? web.json.stringify(
		config.proxy.outbounds ,true
) : "[]"
winform.editOutbounds.modified = false;

winform.editCoreConfig.text = web.json.stringify(
	config.core.default,true
)

winform.enableDpiScaling();
winform.show();
win.loopMessage();
return winform;


text:
import fonts.fontAwesome;
import win.ui;
/*DSG{{*/
var winform = win.form(text="aardio form";right=912;bottom=793;bgcolor=16777215)
winform.add(
advancedConfig={cls="\forms\main\config\advancedConfig.aardio";left=9;top=29;right=902;bottom=739;bgcolor=16777215;db=1;dl=1;dr=1;dt=1;edge=1;z=10};
btnUpdate={cls="plus";text="更新服务器配置";left=687;top=753;right=856;bottom=784;align="left";bgcolor=11580047;db=1;dr=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=22}};iconText='\uF0C7';notify=1;textPadding={left=40};z=5};
editCoreConfig={cls="edit";left=9;top=29;right=902;bottom=739;db=1;dl=1;dr=1;dt=1;edge=1;hide=1;hscroll=1;multiline=1;vscroll=1;z=8};
editOutbounds={cls="edit";left=9;top=29;right=902;bottom=739;db=1;dl=1;dr=1;dt=1;edge=1;hscroll=1;multiline=1;vscroll=1;z=7};
lbTip={cls="static";text="可在下面输入多行服务器分享链接，或JSON数组( 点击字段名会显示用法说明 )：";left=11;top=9;right=899;bottom=29;color=3947580;dl=1;dr=1;dt=1;font=LOGFONT(h=-13);transparent=1;z=6};
navAdvancedConfig={cls="plus";text="代理端口 / 高级配置";left=359;top=750;right=493;bottom=782;border={top=1;color=-16744448};db=1;dl=1;z=9};
navCoreConfig={cls="plus";text="Xray配置 ( JSON )";left=162;top=750;right=293;bottom=782;border={top=1;color=-16744448};db=1;dl=1;z=2};
navOutbounds={cls="plus";text="服务器配置( JSON )";left=31;top=750;right=162;bottom=782;border={top=1;color=-16744448};db=1;dl=1;z=1};
navRight={cls="plus";left=493;top=750;right=762;bottom=751;bgcolor=16777215;border={color=-16744448};db=1;dl=1;dr=1;forecolor=32768;linearGradient=180;z=3};
navSubscription={cls="plus";text="订阅";left=293;top=750;right=359;bottom=782;border={top=1;color=-16744448};db=1;dl=1;z=12};
plus={cls="plus";left=9;top=750;right=31;bottom=751;border={color=-16744448};db=1;dl=1;forecolor=32768;z=4};
subscription={cls="\forms\main\config\subscription.aardio";left=9;top=29;right=902;bottom=739;bgcolor=16777215;db=1;dl=1;dr=1;dt=1;edge=1;z=11}
)
/*}}*/

import config;
import web.json;
import win.ui.tabs;
var tbs = win.ui.tabs(winform.navOutbounds,winform.navCoreConfig,winform.navSubscription,winform.navAdvancedConfig);
tbs.margin = 0;

tbs.skin({
	foreground={
		active=0xFFFFFFFF;
		default=0x00FFFFFF;
		hover=0xFFCCCCCC;
	};
	checked={
		foreground={
			default=0x00FFFFFF;
		};
		border = {
			default = {left=1;right=1;bottom=1;color=0xFF008000}
		};
	}
});

tbs.onSelchange = function(idx,strip,form){
	if(idx==1){
		winform.editOutbounds.hide = false;
		winform.advancedConfig.hide = true;
		winform.editCoreConfig.hide = true;
		winform.subscription.hide = true;

		winform.lbTip.text = `可在下面输入多行服务器分享链接，或JSON数组( 点击字段名会显示用法说明 )：`;
		winform.btnUpdate.text = "更新服务器配置";
		winform.btnUpdate.hide = false;
	}
	elseif(idx==2) {
		winform.editCoreConfig.hide = false;
		winform.editOutbounds.hide = true;
		winform.advancedConfig.hide = true;
		winform.subscription.hide = true;

		winform.lbTip.text = `inbounds[0]，inbounds[1]必须为入站socks、http 代理，outbounds[0] 会转换为出站服务器数组： `
		winform.btnUpdate.text = "更新Xray配置 "
		winform.btnUpdate.hide = false;
	}
	elseif(idx==3) {

		winform.subscription.hide = false;
		winform.editOutbounds.hide = true;
		winform.advancedConfig.hide = true;
		winform.editCoreConfig.hide = true;

		winform.lbTip.text = ``
		winform.btnUpdate.hide = true;
		winform.subscription.loadSubscriptionsOnce();
	}
	else {
		winform.advancedConfig.hide = false;
		winform.editOutbounds.hide = true;
		winform.subscription.hide = true;
		winform.editCoreConfig.hide = true;
		winform.lbTip.text = ``
		winform.btnUpdate.hide = true;
	}
}
tbs.selIndex = 1;

import style;
winform.btnUpdate.skin(style.primaryButton);
winform.btnUpdate.oncommand = function(id,event){
	winform.btnUpdate.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'};

	if(tbs.selIndex==1){
		if(..string.match(winform.editOutbounds.text,"%\[\]")){
			var cfg,err = web.json.tryParse(winform.editOutbounds.text);
			if(!cfg){
				winform.editOutbounds.showErrorTip(,string.concat("JSON语法错误 ",err));
				winform.btnUpdate.disabledText = null;
				return;
			}

			if(cfg){
				cfg = xray.outbounds.validAll(cfg)
			}
			config.proxy.outbounds = cfg : ..table.array();
		}
		else {
			var outbounds = xray.outbounds.importFromString(winform.editOutbounds.text);
			config.proxy.outbounds = outbounds : ..table.array();
			winform.editOutbounds.text = web.json.stringifyArray(outbounds,true,false,true);
		}

		config.proxy.save();
		winform.editOutbounds.modified = false;
		publish("uiCommand.restartXrayCore",cfg)
	}
	elseif(tbs.selIndex==2) {
		var cfg,err = web.json.tryParse(winform.editCoreConfig.text);
		if(type(cfg)!==type.table){
			winform.editCoreConfig.showErrorTip(,string.concat("JSON语法错误 ",err));
			winform.btnUpdate.disabledText = null;
			return;
		}

		if(cfg.outbounds[[1]][["tag"]]!="proxy"){
			winform.msgErr(`outbounds[0]必须指定为{"tag":"proxy"}`);
			winform.btnUpdate.disabledText = null;
			return;
		}

		if(type(cfg.inbounds[[1]][["listen"]])!="string"){
			winform.msgErr(`inbounds[0].listen必须指定为SOCKS代理监听IP或0.0.0.0`);
			winform.btnUpdate.disabledText = null;
			return;
		}

		if(type(cfg.inbounds[[1]][["port"]])!="number"){
			winform.msgErr(`inbounds[0].port必须指定为SOCKS代理监听端口`);
			winform.btnUpdate.disabledText = null;
			return;
		}

		if(type(cfg.inbounds[[2]][["listen"]])!="string"){
			winform.msgErr(`inbounds[1].listen必须指定为HTTP代理监听IP或0.0.0.0`);
			winform.btnUpdate.disabledText = null;
			return;
		}

		if(type(cfg.inbounds[[2]][["port"]])!="number"){
			winform.msgErr(`inbounds[1].port必须指定为HTTP代理监听端口`);
			winform.btnUpdate.disabledText = null;
			return;
		}

		config.core.default = cfg;
		config.core.save();
		publish("uiCommand.restartXrayCore",cfg)
		publish("config.core.changed");
	}

	win.delay(500);
	winform.btnUpdate.disabledText = null;
	winform.msgOk("配置已更新",1200);
}

import xray.outbounds;
winform.editOutbounds.enablePopMenu(function(){
	if(tbs.selIndex==1){
		var strEditBounds = string.trim(winform.editOutbounds.text);
		var menu = {
			{ /*---分隔线---*/ };
			{ "自剪贴板导入 vmess/vless/trojan/ss 链接或订阅源";
				function(id){

					var outbounds = xray.outbounds.importFromClipboard();
					if(#outbounds){
						var json = strEditBounds;
						if( (!#json) || ..string.match(json,"%\[\]")){
							var cfgOutbounds,err = ..table.array();
							if(#json){
								cfgOutbounds,err= web.json.tryParse(strEditBounds);
								if(!cfgOutbounds){
									winform.editOutbounds.showErrorTip(,string.concat("当前配置存在JSON语法错误 ",err));
									return;
								}
							}

							var subscribeUrl = outbounds[1].subscribeUrl;
							if(subscribeUrl){
								for(i=#cfgOutbounds;1;-1){
									var outbound = cfgOutbounds[i]
									if(outbound.subscribeUrl==url){
										..table.remove(	cfgOutbounds,i );
									}
								}
							}

							..table.append(cfgOutbounds,outbounds);
							winform.editOutbounds.text = ..web.json.stringifyArray(cfgOutbounds,true,false,true);
						}
						else {
							var str = xray.outbounds.exportSharedLinks(outbounds);
							if(#strEditBounds){
								winform.editOutbounds.text = strEditBounds+ '\r\n' + str;
							}
							else {
								winform.editOutbounds.text = str;
							}
						}

						winform.msgOk("已成功导入" + #outbounds + "个服务器,请点击「更新设置」启用新配置。",1200);
						winform.editOutbounds.modified = true;
						winform.editOutbounds.setFocus();
						return;
					}

					winform.msgFrown('未导入服务器！\r\n请先复制以下格式文本（自动清除其中的无效内容）：\r\n\r\n1、一行或多行（忽略无效行）分享链接或服务器JSON配置。\r\n支持 vmess://，vless://，ss://，trojan://， trojan-go:// 等通用分享链接。\r\n\r\n2、包含多个服务器配置的JSON数组。\r\n\r\n3、单个 http:// 或 https:// 开头的通用订阅源地址。\r\n订阅源地址支持直接使用github项目文件地址（网址含raw或blob目录名）。\r\n订阅源可以BASE64编码或明文返回以上1、2条规定的配置或分享链接。')
				}
			};
		}

		if( (!#strEditBounds) || ..string.match(strEditBounds,"%\[\]")){
			menu[3] = { "更新当前配置内订阅源";
				function(id){
					var cfg,err = ..table.array();

					var json = strEditBounds;
					if(#json){
						cfg,err = web.json.tryParse(strEditBounds);
						if(!cfg){
							winform.editOutbounds.showErrorTip(,string.concat("JSON语法错误 ",err));
							return;
						}
					}

					var subscribeUrls = {}
					for(i=#cfg;1;-1){
						var outbound = cfg[i]
						if(outbound.subscribeUrl){
							subscribeUrls[outbound.subscribeUrl] = true;
						}
					}

					if(!table.count(subscribeUrls)){
						import win.clip;
						var clibStr = win.clip.read();
						if( ..string.startWith(clibStr,"http://") || ..string.startWith(clibStr,"https://")  ){
							subscribeUrls[clibStr] = true;
						}
						else {
							winform.msgWarn('当前配置不包含来自订阅源的服务器,\n请先复制订阅源网址到剪贴板!');
							return;
						}
					}

					var count = 0;
					for(url,v in subscribeUrls){
						var outbounds = xray.outbounds.importFromString(url);
						if(#outbounds){
							for(i=#cfg;1;-1){
								var outbound = cfg[i]
								if(outbound.subscribeUrl==url){
									..table.remove(	cfg,i );
								}
							}

							count = count + #outbounds;
							..table.append(cfg,outbounds);
						}
					}

					winform.editOutbounds.text = ..web.json.stringifyArray(cfg,true,false,true);

					winform.msgOk("已成功刷新" + count + "个服务器,请点击「更新设置」启用新配置。",1200);
					winform.editOutbounds.modified = true;
					winform.editOutbounds.setFocus();
					return;
				}
			};
		}

		if(..string.match(strEditBounds,"%\[\]")){
			menu[4] = {}
			menu[5] = { "转换为 vmess、vless、trojan、trojan-go、ss 分享链接";
				function(id){
					var cfgOutbouds,err = web.json.tryParse(strEditBounds);
					if(!cfgOutbouds){
						winform.editOutbounds.showErrorTip(,string.concat("JSON语法错误 ",err));
						return;
					}

					for(i,ob in cfgOutbouds){
						if(ob.subscribeUrl){
							if(!winform.msgAsk('当前配置包含订阅源,\n转换为分享链接所有服务器与订阅源将解除关联,\n按确定继续操作，按取消中止。')){
								return;
							}

							break;
						}
					}

					var str = xray.outbounds.exportSharedLinks(cfgOutbouds);
					if(str){
						winform.editOutbounds.text = str;
						winform.editOutbounds.modified = true;
					}
				}
			};
			menu[6] = { "转换为 Base64 订阅源";
				function(id){
					var cfg,err = web.json.tryParse(strEditBounds);
					if(!cfg){
						winform.editOutbounds.showErrorTip(,string.concat("JSON语法错误 ",err));
						return;
					}

					var str = xray.outbounds.exportSharedLinks(cfg);
					if(str){
						import crypt;
						winform.editOutbounds.text = ..crypt.encodeBin(str);
						winform.editOutbounds.modified = true;
					}
				}
			};
		}
		elseif( #strEditBounds ) {
			menu[3] = {}

			menu[4] = { "转换为 JSON";
				function(id){
					import web.json;
					var outbounds = xray.outbounds.importFromString(strEditBounds);
					if(outbounds){
						winform.editOutbounds.text = web.json.stringifyArray(outbounds,true,false,true);
						winform.editOutbounds.modified = true;
					}
				}
			};
		}

		table.push(menu,{ /*---分隔线---*/ });

		table.push(menu,{ "打开配置文件(JSON)";
			function(id){
				import fsys.dlg;
				var path = fsys.dlg.open("*.json|*.json||","打开 json 文件",,winform)
				if(path){
					winform.editOutbounds.text = string.load(path);
					winform.editOutbounds.modified = true;
				}
			}
		});

		table.push(menu,{ "另存为配置文件(JSON)";
			function(id){
				import fsys.dlg;
				var path = fsys.dlg.save("*.json|*.json||","另存为 json 文件",,winform)
				if(path){
					var str = web.json.stringifyArray(
						config.proxy.outbounds,true,false,true
					)

					string.save(path,str )
				}
			}
		});

		table.push(menu,{ "另存为默认服务器列表(JSON)";
			function(id){
				var str = web.json.stringifyArray(
					config.proxy.outbounds,true,false,true
				)

				string.save("/xray-core/winXray-default-servers.json",str );
				winform.msgOk("已存为默认服务器列表 /xray-core/winXray-default-servers.json",1000)
			}
		});

		table.push(menu,{ /*---分隔线---*/ });
		if(winform.editOutbounds.modified){
			table.push(menu,{ "恢复为当前使用的服务器列表(JSON)";
				function(id){
					winform.btnUpdate.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'}
					winform.editOutbounds.text = web.json.stringifyArray(
						config.proxy.outbounds,true,false,true
					)
					winform.editOutbounds.modified = false;

					win.delay(500);
					winform.btnUpdate.disabledText = null;
				}
			});
		}

		table.push(menu,{ "重置为默认服务器列表(JSON)";
			function(id){
				winform.btnUpdate.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'}
				..config.__loadDefaultOutbounds();
				winform.editOutbounds.modified = false;
				publish("uiCommand.restartXrayCore",cfg);

				winform.btnUpdate.disabledText = null;
			}
		});

		table.push(menu,{ "清空服务器列表";
			function(id){
				winform.editOutbounds.text = "[]";
				winform.editOutbounds.modified = true;
			}
		});

		if(  ..string.match(winform.editOutbounds.text,"%\[\]")){

		table.push(menu,{ /*---分隔线---*/ });
			table.push(menu,{ "插入配置字段";{
				{ "代理服务协议:protocol";
					function(id){
						winform.editOutbounds.selText = '"protocol":"vless",'
					}
				};
				{ "SSH管理端口:sshPort";
					function(id){
						winform.editOutbounds.selText = '"sshPort":22,'
					}
				};
				{ "代理服务器地址:address";
					function(id){
						winform.editOutbounds.selText = '"address":"",'
					}
				};
				{ "端口:port";
					function(id){
						winform.editOutbounds.selText = '"port":443,'
					}
				};
				{ "网络协议:network";
					function(id){
						winform.editOutbounds.selText = '"network":"tcp",'
					}
				};
				{ "服务器密码:id";
					function(id){
						winform.editOutbounds.selText = '"id":"",'
					}
				};
				{ "加密方式:security";
					function(id){
						winform.editOutbounds.selText = '"security":"auto",'
					}
				};
				{ "启用TLS:tls";
					function(id){
						winform.editOutbounds.selText = '"tls":"xtls",'
					}
				};
				{ "允许忽略证书:allowInsecure";
					function(id){
						winform.editOutbounds.selText = '"allowInsecure":true,'
					}
				};
				{ "TLS服务器名:sni";
					function(id){
						winform.editOutbounds.selText = '"sni":"",'
					}
				};
				{ "启用TLS:tls";
					function(id){
						winform.editOutbounds.selText = '"tls":"xtls",'
					}
				};
				{ "流控:flow";
					function(id){
						winform.editOutbounds.selText = '"flow":"xtls-rprx-direct",'
					}
				};
				{ "多路复用最大连接:concurrency";
					function(id){
						winform.editOutbounds.selText = '"concurrency":4,'
					}
				};

				{ "伪装类型:type";
					function(id){
						winform.editOutbounds.selText = '"type":"http",'
					}
				};
				{ "备注:ps";
					function(id){
						winform.editOutbounds.selText = '"ps":"",'
					}
				};
				{ "请求主机名:host";
					function(id){
						winform.editOutbounds.selText = '"host":"",'
					}
				};
				{ "请求路径:path";
					function(id){
						winform.editOutbounds.selText = '"path":"/",'
					}
				};
				{ "HTTP请求方法:httpMethod";
					function(id){
						winform.editOutbounds.selText = '"httpMethod":"GET",'
					}
				};
				{ "HTTP请求头:headers";
					function(id){
						winform.editOutbounds.selText = '"headers":{"method":"GET"},'
					}
				};
				{ "绑定的订阅网址:subscribeUrl";
					function(id){
						winform.editOutbounds.selText = '"subscribeUrl":"https://",'
					}
				};
				{ "允许自动测试并连接:autoConnect";
					function(id){
						winform.editOutbounds.selText = '"autoConnect":false,'
					}
				};
				{ "备注:ps";
					function(id){
						winform.editOutbounds.selText = '"ps":"",'
					}
				};
			}});
		}

		return menu;
	}
})

winform.editCoreConfig.enablePopMenu({
	{ /*---分隔线---*/ };
	{ "重置为默认 Xray 配置";
		function(id){
			config.__resetDefaultCore();
			winform.editCoreConfig.text = web.json.stringify(
				config.core.default,true
			)
		}
	};
} )

subscribe("outbounds.updateConfigJson",function(){
	if(winform.editOutbounds.modified){
		return;
	}

	winform.editOutbounds.text = web.json.stringifyArray(
		config.proxy.outbounds,true,false,true
	)

	winform.editOutbounds.modified = false;
} )

winform.editOutbounds.text = web.json.stringifyArray(
		config.proxy.outbounds,true,false,true
) : "[]"
winform.editOutbounds.modified = false;

subscribe("config.inbounds.changed",function(){
	if(winform.editCoreConfig.modified){
		return;
	}

	winform.editCoreConfig.text = web.json.stringify(
		config.core.default,true
	)

	winform.editCoreConfig.modified = false;
} )
winform.editCoreConfig.text = web.json.stringify(
	config.core.default,true
)

var editOutboundsTip = {
	address = {"代理服务器地址";"值可以是域名或者IP地址"};
	alterId = {"额外ID";"仅用于VMESS协议，与服务器一致即可"};
	aid = {"额外ID(alterId)";"数值,仅用于VMESS协议，与服务器一致即可"};
	network = {"传输协议(network)";"可选值为tcp,ws等"};
	net = {"传输协议(network)";"可选值为tcp,ws等"};
	type = {"伪装类型";"默认的可选值为none,http等,kcp或quic此字段表示header.type，可选值为 none,srtp,utp,wechat-video,dtls,wireguard"};
	ps = {"备注";"备注"};
	tls = {"是否启用TLS";"可选值为tls,xtls或空值,vless协议省略时默认值为tls"};
	path = {"请求路径";"用于ws,http等协议指定请求路径"};
	httpMethod = {"HTTP请求方法";"用于http协议指定请求方法"};
	headers = {"HTTP请求头";"用于ws,http等协议指定请求头"};
	id = {"密码";"服务器密码"};
	host = {"主机名";"用于指定http,ws等协议请求头中的主机名"};
	protocol = {"协议";"可选值为 vmess,vless,shadowsocks,trojan,trojan-go 之一"};
	port = {"端口";"服务器端口,数值"};
	security = {"加密方式";"可省略,vmess协议默认为auto,vless协议默认为none"};
	sni = {"TLS服务器名";"TLS客户端请求中的服务器名字段,如果不指定则默认取host或address字段值。"};
	flow = {"流控";"目前仅用于选择 XTLS 的算法,省略时默认值为xtls-rprx-direct"};
	concurrency = {"Mux多路复用:最大并发连接数";"最小值1，最大值1024，特殊值-1，不加载mux模块。缺省由winXray根据连接协议自动设定此值。"};
}

winform.editOutbounds.wndproc = function(hwnd,message,wParam,lParam){
	if(message == 0x202/*_WM_LBUTTONUP*/){
		var lineIndex = winform.editOutbounds.lineFromChar();
		if(lineIndex == winform.editOutbounds.preLineIndex){
			return;
		}
		winform.editOutbounds.preLineIndex = lineIndex;

		var line = winform.editOutbounds.lineText();
		var n,v = string.match(line,`^\s*\"(\w+)\"\s*\:\s*(%"")[\,\s]*$`);
		if( !n ) n,v = string.match(line,`^\s*\"(\w+)\"\s*\:\s*(\d+)[\,\s]*$`);
		if( !n ) n,v = string.match(line,`^\s*\"(\w+)\"\s*\:\s*\{`);
		if(editOutboundsTip[n]){
			winform.editOutbounds.showInfoTip(editOutboundsTip[n][1],editOutboundsTip[n][2])
		}
	}
}

winform.editOutbounds.limit = 0;
winform.editCoreConfig.limit = 0;

winform.enableDpiScaling();
winform.show();
win.loopMessage();
return winform;


text:
import win.ui;
import win.ui.atom;
import fonts.fontAwesome;
/*DSG{{*/
var winform = win.form(text="winXray - 二维码生成工具";right=730;bottom=678;bgcolor=16777215;border="none";exmode="none";mode="popup")
winform.add(
bk={cls="bk";left=-6;top=-2;right=734;bottom=27;bgcolor=12632256;z=10};
btnQrEnode={cls="plus";text="生成二维码";left=562;top=565;right=694;bottom=595;align="left";bgcolor=14935259;db=1;dr=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=22}};iconText='\uF1C4';notify=1;textPadding={left=40};z=9};
editUrl={cls="plus";left=44;top=569;right=537;bottom=594;align="right";bgcolor=16777215;border={bottom=1;color=-8355712};db=1;dl=1;dr=1;editable="edit";font=LOGFONT(h=-16);textPadding={bottom=1};z=8};
lbErrLevel={cls="static";text=" 0：L 可纠错7%数据码字";left=104;top=656;right=272;bottom=679;db=1;dl=1;transparent=1;z=6};
lbVersion={cls="static";text="自动选择版本";left=466;top=656;right=672;bottom=679;db=1;dr=1;transparent=1;z=7};
plus={cls="plus";left=28;top=49;right=708;bottom=522;bgcolor=16777215;db=1;dl=1;dr=1;dt=1;repeat="scale";z=1};
static={cls="static";text="纠错级别:";left=13;top=624;right=97;bottom=651;align="right";db=1;dl=1;transparent=1;z=3};
static2={cls="static";text="版本(大小):";left=353;top=624;right=437;bottom=651;align="right";db=1;dl=1;dr=1;transparent=1;z=5};
static3={cls="static";text="winXray - 二维码生成工具";left=17;top=4;right=230;bottom=24;color=3947580;transparent=1;z=11};
tbErrLevel={cls="trackbar";left=96;top=611;right=290;bottom=641;bgcolor=16777215;db=1;dl=1;max=3;min=0;z=2};
tbVersion={cls="trackbar";left=439;top=611;right=703;bottom=641;bgcolor=16777215;db=1;dr=1;max=40;min=0;z=4}
)
/*}}*/

import style;
winform.btnQrEnode.skin(style.primaryButton);

winform.tbErrLevel.oncommand = function(id,event,pos){

	if( event == 0x8/*_TB_ENDTRACK*/ ){
		pos = winform.tbErrLevel.pos;
		var v = {[0]="L 可纠错7%数据码字";[1]="M 可纠错15%的数据码字";[2]="Q 可纠错25%的数据码字";[3]="H 可纠错30%的数据码字";}
		winform.lbErrLevel.text = pos +"："+ v[pos];
	}
}

winform.tbVersion.oncommand = function(id,event,pos){
	if( event == 0x8/*_TB_ENDTRACK*/ ){
		pos = winform.tbVersion.pos;
		if(!pos)winform.lbVersion.text = "自动选择版本";
		else {
			var width = ((pos-1)*4)+21;
			winform.lbVersion.text = string.format("版本:%d 二维码数据大小:%d x %d",pos,width,width );
		}
	}
}

import win.dlg.message;
import qrencode.bitmap;
winform.btnQrEnode.oncommand = function(id,event){
	if(!#winform.editUrl.text){
		return winform.msgErr("请先输入内容")
	}

	winform.plus.hide = true;
	var qrBmp = qrencode.bitmap(winform.editUrl.text,winform.tbVersion.pos,winform.tbErrLevel.pos );
	if(!qrBmp){
		return winform.msgErr("请先输入内容")
	}
	winform.plus.setBackground(qrBmp.copyBitmap(winform.plus.width));

	winform.plus.hide = false;
	winform.plus.redraw()
}

winform.createQrCode = function(url){
	winform.editUrl.text = url;
	winform.btnQrEnode.oncommand();
}

import win.ui.simpleWindow2;
win.ui.simpleWindow2(winform);

winform.show()
win.loopMessage();


text:
import fonts.fontAwesome;
import win.ui;
import win.ui.atom;
/*DSG{{*/
var winform = win.form(text="youtube 视频下载工具（点击视频链接即可下载）";right=759;bottom=469)
winform.add(
btnVideoInfo={cls="button";text="获取视频下载地址";left=511;top=27;right=732;bottom=60;dr=1;dt=1;font=LOGFONT(h=-16;name='FontAwesome');z=3};
editInfo={cls="richedit";left=29;top=83;right=735;bottom=445;autohscroll=false;db=1;dl=1;dr=1;dt=1;edge=1;link=1;multiline=1;vscroll=1;z=1};
editVideoUrl={cls="edit";text="https://www.youtube.com/watch?v=ZFA5Rax0ypU";left=27;top=29;right=499;bottom=58;dl=1;dr=1;dt=1;edge=1;multiline=1;z=2}
)
/*}}*/

winform.btnVideoInfo.oncommand = function(id,event){
	var url = winform.editVideoUrl.text;
	var vid = string.match(url,"v=([\w\-]+)");
	if(!vid){
		return winform.editVideoUrl.showErrorTip(,"请输入正确的视频地址");
	}

	winform.btnVideoInfo.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'}
	thread.invoke(
		function(winform,vid){
			import web.rest.jsonLiteClient;
			var http = web.rest.jsonLiteClient();

			var videoApi = http.api("https://www.youtube.com/get_video_info?video_id={video_id}");
			var info = videoApi[vid].get();

			if(!info){
				winform.editInfo.text = "网络错误";
				winform.btnVideoInfo.disabledText = null;
			}

			winform.editInfo.text = "";
			winform.editInfo.print( "视频作者：",info.player_response.videoDetails.author);
			winform.editInfo.print( "视频标题：",info.player_response.videoDetails.title);
			winform.editInfo.print( "视频说明：",info.player_response.videoDetails.shortDescription);
			winform.videoTitle = info.player_response.videoDetails.title;

			for(i,v in info.player_response.streamingData.adaptiveFormats){
				if(!string.indexOf(v.mimeType,"audio/mp4")){
					continue;
				}

				winform.editInfo.print();
				winform.editInfo.print("音频质量：",v.quality);
				winform.editInfo.print("音频地址：",v.url);
			}

			for(i,v in info.player_response.streamingData.formats){
				if(!string.indexOf(v.mimeType,"video/mp4")){
					continue;
				}

				winform.editInfo.print();
				winform.editInfo.print("视频质量：",v.qualityLabel);
				winform.editInfo.print("视频地址(含音频)：",v.url);
			}

			var captionTracks = info.player_response.captions[["playerCaptionsTracklistRenderer"]][["captionTracks"]];
			if(info.captions[["playerCaptionsTracklistRenderer"]][["captionTracks"]]){
				if(#captionTracks){
					for(k,v in captionTracks){
						if(v.languageCode=="en"){
							var str  = http.get(v.baseUrl)
							winform.editInfo.print(str);
							break
						}
					}

				}
			}

			winform.editInfo.setFocus();
			winform.btnVideoInfo.disabledText = null;
		},winform,vid
	)
}

import fsys.dlg;
import inet.downBox;
winform.editInfo.onlink=function(message,href){
	if( message = 0x202/*_WM_LBUTTONUP*/ ) {
		var path = fsys.dlg.open("*.mp4|*.mp4","请选择下载路径",,winform,,fsys.path.validName(winform.videoTitle))
		if(!path) return;

		var downBox = inet.downBox(winform,"下载视频");
		downBox.download(href,path);
	}
}

winform.enableDpiScaling();
winform.show();
win.loopMessage();


text:
//RUNAS//github优化
import fonts.fontAwesome;
import win.ui;
/*DSG{{*/
var winform = win.form(text="github.com 网速优化工具";right=655;bottom=281;bgcolor=16777215;border="none";max=false)
winform.add(
bk={cls="bk";left=0;top=0;right=659;bottom=34;bgcolor=12632256;z=8};
cmbIpCidrs={cls="combobox";left=49;top=111;right=328;bottom=137;edge=1;items={};mode="dropdown";z=1};
editIpCurrent={cls="edit";left=118;top=206;right=366;bottom=230;edge=1;z=5};
plusExploreHosts={cls="plus";text='\uF07C 编辑hosts文件';left=450;top=204;right=607;bottom=235;bgcolor=-6371181;font=LOGFONT(h=-15;name='FontAwesome';charset=0);notify=1;z=7};
plusUpdateDns={cls="plus";text='\uF0AD 更改github.com解析到选定IP / 并更新图像等服务器IP';left=77;top=158;right=563;bottom=189;bgcolor=-6371181;font=LOGFONT(h=-15;name='FontAwesome';charset=0);notify=1;z=3};
plusUpdateIps={cls="plus";text='\uF021 获取最新IP列表 / 测速';left=345;top=108;right=607;bottom=139;bgcolor=-6371181;font=LOGFONT(h=-15;name='FontAwesome';charset=0);notify=1;z=2};
static={cls="static";text="本工具使用github官网接口优化http协议访问官网速度（ 适用于无代理连接 ）。";left=49;top=50;right=635;bottom=86;font=LOGFONT(h=-13);transparent=1;z=4};
static2={cls="static";text="当前指向IP:";left=27;top=208;right=111;bottom=228;align="right";transparent=1;z=6}
)
/*}}*/

winform.cmbIpCidrs.onListChange = function(){
	winform.setTimeout(
		function(){
			var ip = string.match(winform.cmbIpCidrs.selText,"\d+\.\d+\.\d+\.\d+");
			if(!ip){
				return ;
			}
			winform.plusUpdateDns.text = "更改 github.com 域名解析到:" + ip
		}
	);
}

import wsock;
import fsys.config;
config  = fsys.config( io.appData("/aardio/github-ip-tools/") );
winform.plusUpdateIps.oncommand = function(id,event){
	winform.editIpCurrent.text = wsock.getIp("www.github.com")

	winform.plusUpdateIps.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'}
	winform.text = "请稍候，正在测试github.com所有可用IP的响应速度"

	var metaInfo = win.invoke(
		function(){
			import win;
			import web.rest.jsonClient;
			var http = web.rest.jsonClient();
			var github = http.api("https://api.github.com/")

			return github.meta.get() :  { git = {
				"192.30.252.0";
				"185.199.108.0";
				"140.82.112.0";
				"13.114.40.48";
				"13.229.188.59";
				"13.234.176.102";
				"13.234.210.38";
				"13.236.229.21";
				"13.237.44.5";
				"13.250.177.223";
				"15.164.81.167";
				"18.194.104.89";
				"18.195.85.27";
				"35.159.8.160";
				"52.192.72.89";
				"52.64.108.95";
				"52.69.186.44";
				"52.74.223.119";
				"52.78.231.108"
			} };
		}
	)

	if(metaInfo[["git"]]){
		config.meta.data = table.map(metaInfo.git,lambda(v)..string.match(v,"^\d+\.\d+\.\d+\.\d+"))
	}
	elseif(!config.meta.data){
		winform.msgboxErr("您的操作系统版本过低")
	}

	var pingThread = function( ip ) {
		import wsock.tcp.client;

		var result = 0;
		for(i=1;4;1){

			var beginTick = ..time.tick()
			var tcp = wsock.tcp.client()

			tcp.setTimeouts(1000,1000);
			if( ! tcp.connectTimeout(ip,80,2) ){
				return;
			};

				sendData =/***********
GET /  HTTP/1.1
Host: www.github.com
Connection: close
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64)
Accept: */*;
Accept-Language: zh-CN,zh;
Accept-Charset:utf-8;
***********/
			tcp.write( sendData + '\r\n\r\n' )

			var rep = tcp.readAll();
			tcp.close();

			if( rep && ..string.startWith(rep,"HTTP/1.1 301 Moved Permanently") ){
				result = result + ((..time.tick() - beginTick)/1000);
			}
			else {
				return;
			}

		}

		return ip,math.round(result/4,2);

	}

	import thread.manage;
	manage = thread.manage(#config.meta.data)

	var ipData = {}
	for(k,ip in config.meta.data){
		manage.createSuspended(pingThread,ip).onEnd = function(ip,sec){
			if(ip && sec!==null ) {
				table.push(ipData,ip + " 响应速度:" + sec + "秒");
				winform.cmbIpCidrs.items = ipData;
				winform.cmbIpCidrs.selIndex = 1;
			}
		}
	}

	manage.resume();
	manage.waitClose();

	winform.cmbIpCidrs.items = ipData;
	winform.cmbIpCidrs.selIndex = 1;
	winform.cmbIpCidrs.onListChange();

	winform.text = "github.com 网速优化工具"
	winform.plusUpdateIps.disabledText = null;
}

import fsys.hosts;
winform.plusUpdateDns.oncommand = function(id,event){
	var ip = winform.cmbIpCidrs.text;
	if(!#ip){
		winform.msgboxErr("请选选择IP地址")
		return;
	}

	ip = string.match(ip,"\d+\.\d+\.\d+\.\d+");
	if( ip == winform.editIpCurrent.text ) return winform.msgboxErr("域名之前已经解析到该IP地址")

	winform.plusUpdateDns.disabledText = "正在更新IP"

	var githubIps = {
		["www.github.com"] = ip;
		["github.com"] = ip;
	}

	import web.rest.jsonLiteClient;
	var http = web.rest.jsonLiteClient();
	var ipApi = http.api("http://ip-api.com/json");
	var data = ipApi["raw.githubusercontent.com"].get();
	if(data[["query"]]){
		githubIps["raw.github.com"] = data[["query"]];
		githubIps["assets-cdn.github.com"] = data[["query"]];
		githubIps["raw.githubusercontent.com"] = data[["query"]];
		githubIps["gist.githubusercontent.com"] = data[["query"]];
		githubIps["cloud.githubusercontent.com"] = data[["query"]];
		githubIps["camo.githubusercontent.com"] = data[["query"]];
		githubIps["avatars0.githubusercontent.com"] = data[["query"]];
		githubIps["avatars1.githubusercontent.com"] = data[["query"]];
		githubIps["avatars2.githubusercontent.com"] = data[["query"]];
		githubIps["avatars3.githubusercontent.com"] = data[["query"]];
		githubIps["avatars4.githubusercontent.com"] = data[["query"]];
		githubIps["avatars5.githubusercontent.com"] = data[["query"]];
		githubIps["avatars6.githubusercontent.com"] = data[["query"]];
		githubIps["avatars7.githubusercontent.com"] = data[["query"]];
		githubIps["avatars8.githubusercontent.com"] = data[["query"]];
		githubIps["marketplace-screenshots.githubusercontent.com"] = data[["query"]];
		githubIps["repository-images.githubusercontent.com"] = data[["query"]];
		githubIps["user-images.githubusercontent.com"] = data[["query"]];
		githubIps["desktop.githubusercontent.com"] = data[["query"]];
	}

	fsys.hosts.ownCacls();
	fsys.hosts.update(githubIps);

	win.delay(1000);
	winform.editIpCurrent.text = wsock.getIp("www.github.com");
	winform.plusUpdateDns.disabledText = null;

	if( ip != winform.editIpCurrent.text ){
		winform.msgboxErr("修改hosts文件失败，如果hosts文件被其他软件锁定时请先解除锁定，也可以搜索网上教程试试设置文件权限")
	}
}

winform.plusExploreHosts.skin(
	background = {
		hover = "/res/images/btn-hover.png";
		focus = "/res/images/btn-focus.jpg";
		active = "/res/images/btn-active.png";
		disabled = "/res/images/btn-disabled.png";
	}
)
winform.plusExploreHosts.oncommand = function(id,event){
	import process;
	process.execute("notepad.exe",fsys.hosts.path,"runas")
}

winform.plusUpdateDns.skin({
	background={
		default=0xFF93C89E;
		hover=0xFF928BB3
	}
})

winform.plusUpdateIps.skin({
	background={
		default=0xFF93C89E;
		hover=0xFF928BB3
	}
})

winform.plusExploreHosts.skin({
	background={
		default=0xFF93C89E;
		hover=0xFF928BB3
	}
})

import win.ui.simpleWindow2;
win.ui.simpleWindow2(winform);

winform.enableDpiScaling();
winform.show()
winform.plusUpdateIps.oncommand();

win.loopMessage();


text:
//GUID生成器
import win.ui;
import win.ui.menu;
import win.guid;
import win.clip;
/*DSG{{*/
var winform = win.form(text="UUID 生成器 - 已生成新的UUID";right=550;bottom=91;border="dialog frame";exmode="none";max=false;parent=...)
winform.add(
btnCpy={cls="button";text="复制到剪贴板";left=436;top=21;right=529;bottom=47;z=3};
btnGenerant={cls="button";text="生成UUID";left=339;top=21;right=432;bottom=47;z=2};
static={cls="static";text="UUID理论上能产生全球唯一的值";left=37;top=63;right=543;bottom=80;transparent=1;z=4};
txtGuid={cls="edit";left=31;top=21;right=327;bottom=47;edge=1;multiline=1;z=1}
)
/*}}*/

winform.btnCpy.oncommand = function(id,event){
	win.clip.write(winform.txtGuid.text)
}

winform.btnGenerant.oncommand = function(id,event){
	winform.txtGuid.text = string.lower( tostring(win.guid.create() ));
}
winform.btnGenerant.oncommand();

winform.enableDpiScaling();
winform.show(true)
win.loopMessage();
return winform;


text:
// 号开头为注释行。
// 本文件用于指定默认服务器列表。
// 本文件可以直接输入json格式的服务器数组（ 服务器可以使用subscribeUrl字段指定订阅源地址，可以指定"autoConnect":false字段禁止自动连接 ）
// 也可以每行一个 vless://、vmess://、trojan、trojan-go、ss:// 等分享链接
[
    {
        "address":"服务器地址",
        "port":443,  // 服务器端口
        "id":"UUID或密码", // v2ray就指定UUID，shadowsocks在这里指定密码
        "network":"tcp", // 网络协议可以改为 "tcp", "ws" 等
        "security":"none", // 加密方法，vmess 默认为 auto, vless 默认为 none, shadowsocks也可以在这里指定加密方法，例如:"aes-256-gcm",
        "protocol":"vless", // 协议可以更换为 "vless","vmess","shadowsocks","trojan","trojan-go" 等
        "host":"伪装主机域名", // 例如ws协议通过这个指定HTTP头里的host字段。
        "sni":"TLS域名",  // 不指定使用host的值，如果host也没指定就使用 address的值
        "tls":"xtls", // 可以不写，值可以指定为 "","tls","xtls" 等
        "flow":"流控", // xtls需要用到的字段，可以不指定使用默认值 "xtls-rprx-direct"
        "ps":"描述文本"
    }
]


text:
{
updateUrl="https://raw.githubusercontent.com/winXray/winXray/master/release/update/";
description="发布 v2.3，修正刷新clash订阅连接重复添加服务器，改进订阅管理等。";
outputDir="/release/update/"
}


text:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly
xmlns="urn:schemas-microsoft-com:asm.v1"
manifestVersion="1.0">
<assemblyIdentity
    processorArchitecture="x86"
    version="5.1.0.0"
    type="win32"
    name="winXray.exe"/>
    <description>winXray</description>
    <dependency>
    <dependentAssembly>
    <assemblyIdentity
        type="win32"
        name="Microsoft.Windows.Common-Controls"
        version="6.0.0.0"
        publicKeyToken="6595b64144ccf1df"
        language="*"
        processorArchitecture="x86"/>
    </dependentAssembly>
    </dependency>
    <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
      <application>
          <!--Windows Vista -->
          <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
          <!--Windows 7 -->
          <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
		  <!--Windows 8 -->
		  <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
		  <!-- Windows 8.1 -->
          <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
          <!-- Windows 10 -->
          <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
      </application>
    </compatibility>
    <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    	<security>
        	<requestedPrivileges>
			<!--
				可任选以下配置之一指定一个进程权限：
				<requestedExecutionLevel level="asInvoker" uiAccess="false" />
				<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
				<requestedExecutionLevel level="highestAvailable" uiAccess="false" />

				requireAdministrator 为管理员权限，
				highestAvailable 为可以获取到的最高权限，
				asInvoker 为默认值,即调用进程当前权限,一般不需要显式指定,指定后会禁用虚拟化。

				虚拟化指Vista以后系统禁止写 Program File目录,启用虚拟化则重定向到%localappdata%\VirtualStore目录，
				而注册表 HKEY_LOCAL_MACHINE\Software 则重定向到HKEY_CURRENT_USER\Software\Classes\VirtualStore\MACHINE\Software
			-->
        	</requestedPrivileges>
    	</security>
	</trustInfo>
	<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >
		<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
			<dpiAware>True/PM</dpiAware>
		</asmv3:windowsSettings>
	</asmv3:application>
</assembly>


text:
//发布前触发
import ide;
io.remove("\..\xray-core\config.json")
io.remove("\..\winXray\xray-core\config.json")


text:
//此触发器在生成EXE以后执行
import ide;
import fsys;

//获取生成的EXE文件路径
var publishFile = ide.getPublishPath();