DSH-2Xray
一个 DSH bundle 插件,扮演 xray-core 进程管理者 + 进程内流量路由者 的双重角色:
- 生成 xray-core 客户端配置,启动并守护
xray子进程; - 在 xray 本地入站通过真实探测之后,把 DSH 进程的全局 HTTP(S) 出口接到该入站;
- 可选:通过零依赖 gRPC 控制面做多节点运行时切换与流量统计。
0.8.0 适配 DSH 0.2.2-rc.1(Node >= 24.9.0);DSH 0.2.1 及以前请使用 0.7.0。
apply(ctx, config)
│
├─ config.js 配置归并 + 字段级校验(失败不进 failed 以外的状态)
├─ xray-config.js 纯函数:多节点 outbound + balancer + VLESS Vision REALITY
├─ supervisor.js 状态机:启动 → 探活 → 接管 → 守护 → 回收/退避
│ ├─ binary.js 解析 xray(插件 bin/ → 数据目录 → PATH)、探测版本
│ ├─ process.js ctx.subprocess(Windows Job Object)/ child_process 兜底
│ ├─ health.js TCP 探活 + 经代理的功能探测
│ ├─ route.js 与 DSH 代理策略层协作,或内置 undici dispatcher 兜底
│ └─ control/ 零依赖 gRPC 控制面(HTTP/2 + 手写 protobuf)
├─ tools.js xray_status / xray_switch_node / xray_stats(可选)
└─ paths.js 数据目录、原子写、哈希(只记录配置哈希,不记录密钥)
关键设计决策(以及为什么不是骨架里的写法)
| 主题 | 结论 | 原因(已在 DSH 0.2.1-rc.3 上核实) |
|---|---|---|
| 全局接管方式 | 调用 @deepseek-ai/dsh-http-proxy 的 installProxyFromEnvironment(),不自己 setGlobalDispatcher() |
DSH 在第一个插件挂载前就装好了策略 dispatcher;web_fetch 只问 proxyRouteFor()。裸装 dispatcher 会造成「模型 API 进代理、web_fetch 仍直连」的双源真值分裂。 |
| 入站协议 | 默认 HTTP,SOCKS 只作为可选附加 | DSH 代理策略明确拒绝 socks5:// URL(诊断为 “connecting directly”)。SOCKS 只能靠内置 dispatcher,且失去 web_fetch 覆盖。 |
| 清理钩子 | ctx.effect(() => disposer),apply 返回它 |
Cordis 没有 ctx.on('dispose');ctx.effect 与 apply 返回值才是生命周期 seam。 |
| 子进程 | 优先 ctx.subprocess |
Windows 上是 Job Object(KILL_ON_JOB_CLOSE)。Electron 关闭时对运行时发 SIGTERM ≈ TerminateProcess,cordis dispose 可能根本不执行;没有 Job Object 就会留下孤儿 xray 占着端口。 |
| 就绪顺序 | 先起进程 → TCP 探活 → 功能探测 → 才接管 | 反过来会出现「dispatcher 指向尚未监听的端口」,整个 DSH 的 fetch 全部 ECONNREFUSED。 |
| 停止顺序 | 先摘路由 → 再 terminate → 等退出 | 反过来存在「进程已死、dispatcher 还指着它」的窗口。 |
| 端口 | 固定端口 + 占用即失败,不静默换端口 | 换端口后 DSH 仍指向旧端口,表现为持续 ECONNREFUSED,且用户无从判断。 |
| 多节点切换 | 预置全部 outbound + balancer + RoutingService.OverrideBalancerTarget |
HandlerService.AddOutbound 要传 protobuf 编码的 OutboundHandlerConfig(TypedMessage sender/proxy settings);override 只传两个字符串,是最小可靠面。 |
| 控制面实现 | 零依赖:node:http2 + 手写 protobuf 编解码 |
为一个 balancer 切换 + 统计查询给每个 profile 装 @grpc/grpc-js + proto-loader 不划算,也扩大供应链面。 |
| 重启 | 只对「意外崩溃」退避重启 | 退出码 23(配置错)不重启;端口占用不重启;上游不可达不重启 xray(进程是健康的)。 |
| 工具注册 | ctx.inject(['tools']) 可选注册 |
若声明 inject = ['tools','settings'],缺少任一服务的 profile 会整行不激活;代理本身不应该被工具可用性拖累。 |
安装
插件是一个 Host-only bundle,无需构建;除控制面外无运行时依赖。
- 安装:
npm i @twinsearth/dsh-2xray后由插件管理器启用;或在 DSH 插件页选择「从目录安装」,指向E:\GPT\dsh-2xray。 - 把 xray 可执行文件放到下面任一位置(按解析优先级):
- 插件自己的
bin/xray(.exe)(随 bundle 分发时用;见 bin/README.md 的 MPL-2.0 说明) - 配置
binary.path - 环境变量
DSH_XRAY_BIN <DSH_HOME>\xray-proxy\bin\xray.exe(DSH_HOME默认%USERPROFILE%\.dsh)- 放进
PATH
- 插件自己的
- 编辑 profile 的
cordis.patch.yml(或 bundle 默认配置),填入server.*或nodes[],把enabled改成true,重启 profile。
bundle 自带的 cordis.patch.yml 默认 enabled: false,避免安装即报错;它是整块配置,profile 层覆盖时必须重述要保留的字段。注册行的 name 必须等于包名(@twinsearth/dsh-2xray),否则 patch 会按「name mismatch」跳过。
- id: xray-proxy
name: '@twinsearth/dsh-2xray'
config:
enabled: true
server:
address: example.com
port: 443
uuid: 00000000-0000-0000-0000-000000000000
flow: xtls-rprx-vision
serverName: www.example.com
password: REPLACE_WITH_43_CHAR_BASE64URL_X25519_PUBLIC_KEY
shortId: ''
fingerprint: chrome
spiderX: /
inbound:
protocol: http
host: 127.0.0.1
port: 20808
takeover: auto
配置参考
| 字段 | 默认 | 说明 |
|---|---|---|
enabled |
false |
总开关 |
server.address |
— | 服务端域名或 IP(旧版单节点写法;多节点用 nodes) |
server.port |
443 |
服务端端口 |
server.uuid |
— | VLESS uuid;非标准 UUID 但 ≤30 字节也可 |
server.flow |
xtls-rprx-vision |
"" / xtls-rprx-vision / xtls-rprx-vision-udp443;非空时 network 必须是 raw/tcp 且 security 必须是 reality/tls(-test 不校验,插件静态拦截) |
server.serverName |
— | REALITY/TLS 的 SNI |
server.password |
— | REALITY 公钥(当前字段名),43 字符 base64url |
server.publicKey |
— | REALITY 公钥旧字段名;与 password 同时写可跨版本,至少提供一个 |
server.shortId |
"" |
偶数长度 hex,≤16 字符 |
server.fingerprint |
chrome |
uTLS 指纹 |
server.spiderX |
/ |
以 / 开头 |
server.network |
raw |
raw / tcp(后者归一为 raw) |
server.security |
reality |
reality / tls / none |
nodes |
[] |
节点数组;与 server.* 二选一。每项支持 id/tag 与全部 server.* 字段 |
shareLink |
"" |
粘贴一条 vless:// 分享链接(二维码里就是这段文本),解析后追加为节点 |
shareLinks |
[] |
多条分享链接;优先于 shareLink,与显式 nodes 合并(显式节点在前) |
activeNode |
第一个节点 | 启动时钉住的 node id 或 outbound tag |
inbound.protocol |
http |
http 或 socks;socks 只建议给非 Node 客户端 |
inbound.host |
127.0.0.1 |
只允许回环地址 |
inbound.port |
20808 |
本地入站端口 |
inbound.socks.enabled |
false |
额外开 SOCKS 入站 |
inbound.socks.port |
20809 |
不能与 HTTP 入站相同 |
inbound.socks.udp |
false |
undici 只实现 SOCKS CONNECT,开启只服务其它程序 |
takeover |
auto |
auto:已有代理策略时不覆盖;force:强制覆盖;off:只起 xray 不接管 |
control.enabled |
false |
开启 gRPC 控制面(api 入站 + services) |
control.host / control.port |
127.0.0.1 / 10085 |
只允许回环;端口不能与任何入站冲突 |
control.services |
Handler/Stats/Routing | 传给 xray 的 api.services |
control.stats |
true |
打开 stats 与 policy.system 流量计数 |
control.balancerTag |
active |
多节点 balancer 的 tag,也是切换目标 |
control.timeoutMs |
3000 |
每次 gRPC 调用超时 |
noProxy |
"" |
追加的 bypass 列表(回环由 DSH 强制加入);不支持 CIDR |
routing.bypassDomains |
内网后缀 | 直连域名后缀列表,数组或逗号串 |
routing.blockAds |
false |
加 geosite:category-ads-all → block |
routing.geositeCn |
false |
国内域名直连(需要 geosite 数据) |
binary.path |
"" |
显式 xray 路径 |
binary.dataDir |
"" |
数据目录;默认 <DSH_HOME>/xray-proxy |
lifecycle.startTimeoutMs |
15000 |
端口就绪预算 |
lifecycle.stopGraceMs |
3000 |
终止宽限(也是 ctx.subprocess 的 graceMs) |
lifecycle.maxRestarts |
5 |
崩溃重启上限,超过进 restart-exhausted |
lifecycle.backoffMs / backoffMaxMs |
500 / 30000 |
指数退避 |
lifecycle.healthIntervalMs |
30000 |
周期健康探测;0 关闭 |
lifecycle.unhealthyThreshold |
3 |
连续失败后回收进程 |
functionalProbe.enabled |
true |
经本地入站请求一次 204;这是唯一能证明 uuid/flow/SNI/REALITY 全链路可用的检查 |
functionalProbe.url |
http://www.gstatic.com/generate_204 |
探测目标 |
functionalProbe.timeoutMs |
5000 |
探测超时 |
log.level |
warning |
传给 xray 的 loglevel;访问日志固定 none |
多节点与 gRPC 控制面
nodes[] 与 server.* 二选一:前者为每个节点生成一个 outbound + 一个 balancer;后者保持单节点旧行为,配置字节不变。
- id: xray-proxy
name: '@twinsearth/dsh-2xray'
config:
enabled: true
activeNode: hk-01 # node id 或 outbound tag
nodes:
- id: hk-01
address: hk.example.com
port: 443
uuid: 11111111-2222-4333-8444-555555555555
flow: xtls-rprx-vision
serverName: www.example.com
password: REPLACE_WITH_43_CHAR_BASE64URL_X25519_PUBLIC_KEY
shortId: ''
- id: jp-01
address: jp.example.com
port: 443
uuid: 11111111-2222-4333-8444-555555555555
flow: xtls-rprx-vision
serverName: www.example.com
password: REPLACE_WITH_43_CHAR_BASE64URL_X25519_PUBLIC_KEY
shortId: ''
control:
enabled: true
port: 10085
stats: true
启动顺序:写配置 → 起进程 → TCP 就绪 → 204 功能探测 → 接管 dispatcher → OverrideBalancerTarget(activeNode) 钉住出口。切换节点只改 balancer override,进程、入站、dispatcher 都不动。
模型可调用的工具(ctx.tools.register + 从安装目录加载 @deepseek-ai/dsh-tools 的 defineTool):
| 工具 | 作用 | 依赖 |
|---|---|---|
xray_status |
与 status.json 相同的状态快照 |
无 |
xray_validate_config |
用 xray run -test 预检当前配置;不启动/不重启进程;返回 { ok, reason, detail, configHash } |
无 |
xray_switch_node |
{node} → 钉住指定 outbound |
control + RoutingService |
xray_stats |
每节点累计上下行字节;{reset:true} 读后清零 |
control + StatsService + stats |
版本要求:Xray 2024-09 之后的版本(v1.8.24+ / v24.9+)。 本机 1.4.5 实测 RoutingService 返回 12 unknown service,而 StatsService 在 1.4.5 已可用。版本过老时插件仍正常启动,切换工具会给出可读诊断并把 status.control.reachable 置为 false。
注意:
- override 只存在于 xray 进程内存,重启后由插件按
activeNode重新钉住;切换结果不写回配置(持久化需要设置页ctx.settings,尚未实现)。 - API 入站只绑
127.0.0.1,但无认证:本机任意进程可查统计或切节点。需要更强隔离时用随机端口,或关闭control.enabled只保留静态多出站。 stats计数器是自进程启动以来的累计值,不是速率;速率需要调用方自行差分。
界面入口:设置页 + 左侧 2Xray 面板
v0.2.0 提供两个入口,互不替代:
- 插件设置页(设置 → 插件 → Xray 代理):Schemastery 表单,可编辑
启用、分享链接 (vless://)、xray 路径、本地端口、takeover、功能探测、控制面。 保存后写入 profile patch,重启 DSH 生效。 - 左侧菜单「2Xray」(插件标签下方,黑色地球图标):内含 二维码图片识别、屏幕共享扫码、链接复制;识别后复制到设置页的「分享链接」保存。
设置 → 插件 → Xray 代理 ← 粘贴 / 保存 / 开关
左侧 2Xray(地球图标) ← 扫码 / 复制
0.8.0 起设置页内置 jsQR:
BarcodeDetector不可用(部分 Windows Chrome)时自动回退; 图片导入与屏幕扫码都不再需要打开tools/scanner.html。
配置导入:分享链接 / 二维码
三种入口最终都归约为同一段 vless:// 文本,所以解析器只有一份(lib/share-link.js),UI 只负责把文本拿到手。
| 方式 | 状态 | 实现 |
|---|---|---|
| 粘贴分享链接 | ✅ Host 侧已实现 | shareLink: "vless://...",或 node scripts/parse-share-link.mjs "<link>" 生成 nodes JSON |
| 扫描屏幕二维码 | ✅ 设置页 | getDisplayMedia() 取帧 + BarcodeDetector → 内置 jsQR 回退 |
| 扫描图片二维码 | ✅ 设置页 | <input type="file"> + createImageBitmap() + 同一个双解码器 |
- id: xray-proxy
name: '@twinsearth/dsh-2xray'
config:
enabled: true
# 支持标准 Xray/v2rayN 导出格式:
# vless://uuid@host:443?security=reality&sni=...&fp=chrome&pbk=...&sid=...&spx=%2F&flow=xtls-rprx-vision#Hong%20Kong
shareLink: "vless://11111111-2222-4333-8444-555555555555@hk.example.com:443?security=reality&sni=www.example.com&fp=chrome&pbk=REPLACE_43_CHARS&sid=ab12&spx=%2F&flow=xtls-rprx-vision&type=tcp#Hong%20Kong"
control:
enabled: true
解析规则:
#片段名 → 节点id(如Hong Kong→ outbound tagproxy-Hong_Kong),可用作activeNode。sni→serverName;pbk→password与publicKey双写(跨版本兼容);sid→shortId;fp→fingerprint;spx→spiderX。security=none时清空 flow/SNI/shortId;type=tcp归一为raw。vmess:///trojan:///ss://会明确报「暂不支持」,而不是生成一个 Xray 会拒绝的配置——当前 builder 只产出 VLESS 出站。
订阅分组(订阅 URL 拉取多节点)未实现。 它是独立的网络动作(SSRF、重定向、离线缓存、订阅格式),建议单独设计,不与本地导入混在一起。
扫码能力依赖浏览器设置页(dsh.client + configForms/slots),当前仓库是 Host-only,所以只有链接字段与 CLI 可用。
状态机与失败原因码
对外状态:idle → resolving → starting → ready;异常路径为 degraded / backoff / failed / stopped。
状态快照写入 <dataDir>/status.json(0600,不含密钥),字段包括:
state、reason、detail、generation、restarts、port、binary{path,source,version}、process{pid,alive,exitCode,stderrTail}、route{installed,mode,proxy,webFetchCovered,sentinel,dispatcherIsOurs}、nodes[]、activeNode、control{enabled,reachable,activeTarget,lastError}、functionalProbe、configHash、startedAt、updatedAt。
原因码:
| reason | 含义 | 是否自动重启 |
|---|---|---|
disabled |
enabled: false |
— |
config-invalid |
字段校验失败(detail 带字段名) | 否,改配置后重载 |
binary-missing / binary-unusable |
找不到 xray,或 xray version 不能运行 |
否 |
config-write-failed |
数据目录不可写 | 否 |
port-busy |
入站端口被占用 | 否(不换端口) |
config-rejected |
xray run -test 失败,或运行中以退出码 23 退出 |
否 |
start-timeout |
进程活着但端口未监听 | 否 |
functional-probe-failed |
入站通了但经代理的请求失败 | 否 |
route-install-failed |
xray 健康,但策略安装失败 → degraded |
进程保留 |
external-proxy-active |
takeover: auto 检测到启动器已有代理 → degraded |
— |
crashed |
运行中意外退出 → backoff |
是,指数退避 |
unhealthy |
连续健康探测失败,回收后重启 | 是 |
restart-exhausted |
超过 maxRestarts |
否 |
disposed |
插件卸载/DSH 退出 | — |
覆盖矩阵
| 流量 | 是否经 xray |
|---|---|
模型 API(globalThis.fetch,含 SSE) |
✅ |
web_fetch |
✅(harness 模式);❌ builtin/SOCKS 模式(已知缺口) |
web_search、MCP HTTP、账号登录、第三方插件 fetch |
✅ |
| 回环地址(DSH Web UI、本地服务) | ❌ 强制直连(DSH 策略内置,插件不重复实现) |
noProxy 命中的域名 |
❌ |
内网 IP / geoip:private |
❌ 由 Xray routing 直连 |
| Electron 更新检查、内嵌视图(Chromium 网络栈) | ❌ |
ws 远程访问 / api-gateway |
❌ 自行建连,不走 undici |
OTLP 遥测(node:http) |
❌ |
| worker / PTC 脚本 | ❌ 宿主刻意不注入 |
| bash/curl/git 子进程 | ⚠️ 接管后,DSH 从当前策略派生子进程 overlay,新起的子进程会拿到无认证的 http://127.0.0.1:<port>;是否生效取决于该程序是否读 HTTP_PROXY |
安全
- 生成的 xray 配置含 uuid 与 REALITY 公钥,写入
<dataDir>/conf/xray-config.json,POSIX 权限 0600、目录 0700;Windows 上 ACL 继承用户目录,插件不做icacls收紧(已知边界)。 - 只记录
configHash,不记录配置内容;错误信息只带 stderr 尾部。 - 入站只允许回环地址;
inbound.host: 0.0.0.0会被校验拒绝。 - 凭据不写进
process.env:installProxyFromEnvironment收到的代理 URL 是无认证的http://127.0.0.1:<port>。 - 控制面 API 无认证(仅回环);高隔离场景请关闭或换随机端口。
已知限制
- builtin/SOCKS 模式不覆盖
web_fetch(它会自建 pinned-IP Agent)。默认 HTTP + harness 模式没有这个缺口。 - 没有设置页 UI:v1 通过
cordis.patch.yml配置。要做表单需走ctx.settings+ Schemastery +configForms,且可编辑字段必须是 volatile。 - 不下载 xray:插件可以携带
bin/xray,但从不联网下载;供应链(版本、哈希、签名)留给发布流程。 - 重启会打断在途流:Windows 上
taskkill /F无法 drain;DSH 会看到TRANSPORT重试,或干净 FIN 导致的STREAM_CLOSED(默认不重试)。xrayconnIdle已设为 600s,尽量让 DSH 的 300s 空闲看门狗先触发。 - 端口变化必须重启 DSH:插件不改端口,也不在运行中重装策略。
- 多实例:每个 DSH profile 独立端口/配置;没有跨实例锁,端口冲突会明确报
port-busy。 - 控制面依赖较新 Xray:
RoutingService需要 2024-09 之后的版本;更老的版本只有StatsService。失败时插件不崩溃,但切换工具不可用。 - API 无认证:只绑回环,但本机任意进程都能调用。高隔离场景请关闭
control.enabled或换随机端口。 - 切换不持久化:
xray_switch_node只改进程内 override,重启后回到activeNode;写回配置需要设置页/ctx.settings(未实现)。
验证结果(诚实边界)
已执行:
| 项 | 结果 |
|---|---|
单元测试 node --test --test-isolation=none test/*.test.js |
199/199 通过(配置校验、单/多节点配置生成、分享链接解析、二进制解析优先级、路由安装/回滚/幂等、状态机启动/崩溃重启/退出码 23/端口占用/停止顺序/启动竞态、protobuf 编解码、gRPC 客户端对 mock h2 服务、工具注册与调用) |
DSH 0.2.2-rc.1 compat 探针 DSH_APP_ROOT=... node --test --test-isolation=none test/compat/*.test.js |
12/12 通过(真实 cordis 4.1.0 / dsh-http-proxy / subprocess / client-modules / 工具 schema) |
真实运行时加载(ELECTRON_RUN_AS_NODE=1 "E:\DS\DeepSeek Harness.exe") |
插件入口可导入;ctx.effect 注册与 dispose 正常;state=idle → stopped |
真实运行时路由探针(scripts/probe-routing.mjs) |
通过 createRequire 解析到安装目录里的 @deepseek-ai/dsh-http-proxy;installProxyFromEnvironment 安装成功;fetch 实测经假代理(absolute-form 请求行)、回环地址直连(origin-form)、restore() 后回到直连(ENOTFOUND) |
| 工具层解析 | @deepseek-ai/dsh-tools 同样从安装目录解析成功,defineTool 生成合法参数 schema |
| 真实 xray gRPC 探针 | 启动本机 xray 1.4.5 的 API 入站后,插件的零依赖客户端成功调用 StatsService.QueryStats 并解码出 inbound 计数;RoutingService 按预期返回 12 unknown service |
| 多节点配置结构校验 | 2 节点 + balancer + API 入站 + stats 的配置,被本机 xray 1.4.5(降级 stream 后)判为 Configuration OK. |
| xray 配置结构校验 | 单节点同款做法通过,证明 inbound/outbound/routing/policy 骨架合法 |
未验证(必须在目标机执行):
- 与 Xray v26.10.x 真实二进制的
run -test(REALITYpassword、network: raw字段只能靠源码规格保证); - 多节点
OverrideBalancerTarget在支持RoutingService的真实版本上的端到端切换(本机 1.4.5 没有该服务); - 真实服务端的端到端连通(uuid/flow/SNI/shortId/公钥);
- Windows Job Object 在强杀 DSH 后的 xray 清理;
web_fetch实抓流量与proxyRouteFor()的一致性;- 验收测试计划中的 83 条用例(见仓库根目录
DSH-XRAY-PROXY-ACCEPTANCE-TEST-PLAN.md)。
排障
| 现象 | 处理 |
|---|---|
binary-missing |
把 xray.exe 放进插件 bin/、<DSH_HOME>\xray-proxy\bin\,或设 binary.path |
config-rejected,detail 含 encryption |
检查生成的配置未被手工改坏;users[0].encryption 必须为 "none" |
config-rejected,detail 含 password |
用 xray x25519 重新生成,确认 43 字符 base64url |
functional-probe-failed |
先用 curl -x http://127.0.0.1:20808 https://example.com -v 手工验证;确认服务端参数;离线环境可临时设 functionalProbe.enabled: false |
port-busy |
释放端口(可能有上次残留的 xray),或改 inbound.port 后重启 DSH |
external-proxy-active |
启动环境里已有 HTTP(S)_PROXY;需要覆盖时设 takeover: force |
模型请求突然 ECONNREFUSED |
查看 status.json 的 state/reason;若 backoff,等待重启或修复配置 |
状态一直 degraded + route-install-failed |
查日志里的模块解析诊断;必要时确认 DSH 安装目录结构 |
xray_switch_node 报 unknown service ... RoutingService |
xray 版本早于 2024-09;升级二进制,或关闭 control.enabled 只用 activeNode |
平台内核包
@twinsearth/dsh-2xray 通过 optionalDependencies 引用按平台拆分的核心包(例如 @twinsearth/dsh-2xray-core-win32-x64),npm 只安装当前平台那一个。本地没有可用内核包时,仍按 插件 bin/ → 数据目录 → v2rayN 发现 → PATH 回退,或用 tools/install-xray-core.mjs 安装。
维护者用 tools/build-core-packages.mjs 从官方 zip 生成平台包;.github/workflows/core-packages.yml 会在具备 NPM_TOKEN secret 时自动下载、校验并发布各平台包。
规则数据自动更新
设置页默认勾选「每 72 小时更新规则数据」:Host 会下载 XTLS 的 geoip-lite.dat / geosite.dat,失败时回退 jsDelivr(Loyalsoldier);文件小于 100KB 视为错误页并拒绝替换。首次缺失时在启动前下载,之后每 72 小时检查一次,更新成功后重启 xray 热加载。
Xray 26.9.30 接受顶层
geodata配置块但实测不执行其 cron(85 秒内无下载),因此更新由插件完成,不依赖该字段。
健康检查
- 进程内(需 DSH 会话鉴权):
GET /api/xray/healthz→{ "ok": true, "version": "v0.8.0", ... } - 发布物(公开、静态):Release 资产
healthz.json,三字段{ ok, service, version },version与 tag 一致(0.8.0 →v0.8.0)。
DSH 插件市场
索引地址(format 1):
https://raw.githubusercontent.com/TwinsEarth/DSH-2Xray/main/market/index.json
在 DSH 的「设置 → 插件市场 → 添加源」粘贴该地址,即可搜索并安装 DSH-2Xray。索引只声明 npm 包 @twinsearth/dsh-2xray 与其版本,安装仍走 npm registry。
更新
设置页 2Xray 里有三个入口:
- 检测更新:查询 npm 上的
latest,显示当前/最新版本。 - 立即更新:下载并校验 npm tarball(sha512),原子替换插件目录;重启 DSH 后生效。
- 自动更新:启动 5 秒后检查并安装新版本(默认关闭;同样需要重启)。
Host API:GET/POST /api/xray/update(走 ctx.connection.fetch 鉴权围栏)。
TUN 模式(参考 v2rayN)
依赖文件
- Windows:插件自带
bin/wintun.dll(Wintun 0.14.1,x64,GPL-2.0,见bin/WINTUN-NOTICE.txt)。启用 TUN 时会自动复制到 xray 可执行文件旁,不需要安装 v2rayN。 - macOS / Linux:TUN 使用系统
utun/tun 设备,不需要 wintun,只需要 root 权限。 - 缺少 xray 核心时:插件会自动下载当前平台的官方 Xray release(校验
.dgstSHA2-256)到数据目录的bin/,macOS arm64/x64、Linux x64/arm64 均已覆盖;可用binary.autoInstall: false关闭。
DSH 内置浏览器是 Chromium,不读插件的 undici dispatcher;只加启动参数能覆盖浏览器,但 DNS 仍可能被本地污染。v2rayN 的做法是 虚拟网卡接管全机流量 + 由 Xray 远程解析 DNS,v0.4.2 提供了同样的可选模式:
tun:
enabled: true # 默认 false
name: dsh-xray-tun
mtu: 1500
开启后 xray 配置会多出:
protocol: tun入站(10.0.0.1/16网关、0.0.0.0/1+128.0.0.0/1路由);- 顶层
dns:DoHhttps://1.1.1.1/dns-query/https://dns.google/dns-query; protocol: dns路由到dns-out,应用不再使用本地被污染的 DNS。
要求:
- 管理员/root 权限(创建虚拟网卡);
- Windows 需要
wintun.dll(放在 xray 可执行文件旁,或从 v2rayN 目录复制); - 开启后重启 DSH;缺少权限或 dll 时插件会以
tun-admin-required/tun-wintun-missing明确失败,而不是静默。
不想用 TUN 时,仍可用
tools/launch-with-browser-proxy.cmd仅给 Chromium 加--proxy-server;TUN 解决的是「全机 + DNS 污染」。
路由模式与黑白名单
global(默认):全部流量走 2Xray。cn-direct:geoip:cn/geosite:cn直连,其余走代理。插件会从 xray 二进制旁复制geoip.dat/geosite.dat到数据目录,并设置XRAY_LOCATION_ASSET。whitelist:仅白名单域名走 2Xray,其余直连。
黑名单优先于白名单与分流,并会同时加入 DSH 的 noProxy(xray 重启时也不受影响)。
- 默认白名单:github.com、google.com、youtube.com、x.com、facebook.com、api.github.com、raw.githubusercontent.com
- 默认黑名单:deepseek.com、chat.deepseek.com、api.deepseek.com、download.deepseek.com、api-docs.deepseek.com、platform.deepseek.com、registry.npmjs.org
默认值只对没有显式配置的 profile 生效;已有配置请在设置页按上面列表更新后重启。
内核安装(macOS / Linux)
npm 包不内嵌五平台内核。未找到内核时:
node tools/install-xray-core.mjs 26.9.30
脚本下载官方 release 资产、校验 .dgst 的 SHA2-256、解压到 ~/.dsh/xray-proxy/bin/。GitHub 不可达时设置 DSH_XRAY_MIRROR 指向镜像。macOS 也会自动搜索 ~/Library/Application Support/v2rayN/bin/xray/xray。
Chrome 扩展(2Xray for Chrome)
自 0.7.0 起与本插件同版本发布(双版本):仓库内的 chrome-extension/ 是 Manifest V3 浏览器伴侣。
- 把 Chrome 流量指向本地 Xray HTTP/SOCKS 入站(默认
127.0.0.1:20808,与插件默认值一致); - 三卡片面板:连接状态 / 当前 VLESS 节点 + 分享二维码 / 路由与最近日志;
- PAC 分流:全局、白名单、CN 直连(浏览器侧近似
.cn+ 常见国内站点;完整 geosite 仍由内核cn-direct负责); vless://链接导入、图片/摄像头扫码、生成独立config.json(VLESS + 本地 inbound + 路由);- 黑名单永远直连,默认带
deepseek.com系列与registry.npmjs.org。
安装:chrome://extensions → 开发者模式 → 加载已解压的扩展程序 → 选择 chrome-extension/(或解压 Release 中的 2xray-chrome-v0.8.0.zip)。
固定扩展 ID:jcalgojagadmmjeolgahhmhnmhdkecna。
扩展不能启动/停止
xray进程,也不能接管系统全局流量;内核由本插件、v2rayN 等提供,系统级代理请用 TUN 模式。详见 chrome-extension/README.md。
构建与测试:
node --test --test-isolation=none test/extension-*.test.js
node scripts/build-extension.mjs # dist/2xray-chrome-v0.8.0.zip
独立版(无需 DSH,Windows)
如果用户没有安装 DSH,可以直接使用 2Xray 独立版:一个压缩包内含 Xray 内核、配置生成脚本、登录启动项和 Chrome 扩展,安装后浏览器即可走本地 Xray。
- 发布资产:
2xray-standalone-win64-v0.8.0.zip - 安装:解压 → 双击
install.cmd→ 粘贴vless://链接 → 按提示在chrome://extensions加载安装目录里的extension/ - 默认:
127.0.0.1:20808、cn-direct分流、仅监听回环、不改系统代理;可关闭登录启动 - 管理:安装目录里的
start.cmd/stop.cmd/uninstall.cmd - 详细说明:standalone/windows/README.md
构建独立版(需要一份官方 Xray 26.9.30 的 Windows 文件):
node scripts/build-standalone.mjs --core "$env:USERPROFILE\.dsh\xray-proxy\bin"
# dist/2xray-standalone-win64-v0.8.0.zip
macOS/Linux 用户可直接使用同一 Chrome 扩展 + 任意本地 Xray(监听
127.0.0.1:20808);独立的 Unix 安装器尚未打包。
内置浏览器(Chromium)
DSH 内置浏览器走 Chromium 网络栈,不读 undici dispatcher,插件无法在运行时改它的代理。支持方式只有启动参数:
& "E:\DS\DeepSeek Harness.exe" --proxy-server=http://127.0.0.1:20808
或运行 tools/launch-with-browser-proxy.cmd(自动查找 DSH 路径并透传 --proxy-server)。
必须先完全退出 DSH:单实例再次启动只会聚焦旧窗口,不会应用参数。系统代理/TUN(v2rayN)也能让 Chromium 生效,但那是全机代理,与本插件无关。
开发
cd E:\GPT\dsh-2xray
node --test --test-isolation=none test/*.test.js
在受限沙箱里不要用
node --test test/:它按文件spawn子进程并使用管道,会被拒绝(EPERM);--test-isolation=none在进程内运行。
可复跑的验证脚本:
| 脚本 | 用途 |
|---|---|
scripts/check-config.mjs |
用真实 xray 二进制校验生成的配置;--legacy 兼容 1.8 之前的 tcp 命名 |
scripts/probe-routing.mjs |
在 DSH 运行时验证「fetch 被接管 / 回环直连 / restore 后恢复」 |
完整证据与未验证项见 docs/EVIDENCE.md;验收闸门 0 的接管方式声明见 docs/AC-0.md。
No comments yet. Be the first to write one.