@twinsearth/dsh-2xray

devtested: passnpmMIT

Xray-core supervisor and router for DSH with VLESS import, global/China-direct/whitelist routing, a three-card live panel, and self-update.

下载量 / Downloads
4,485
依赖体积 / node_modules
1.79 MB
测试时间 / Tested
2026-11-06
DSH 版本 / Version
0.2.2-rc.1

Install

dsh plugin --profile web add @twinsearth/dsh-2xray

Sandbox test results

StageResult
1 install✓
2 list✓
3 mount✓
4 boot—

Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).

README

DSH-2Xray

![CI](https://github.com/TwinsEarth/DSH-2Xray/actions/workflows/ci.yml)

![npm](https://www.npmjs.com/package/@twinsearth/dsh-2xray)

![MIT](LICENSE)

一个 DSH bundle 插件,扮演 xray-core 进程管理者 + 进程内流量路由者 的双重角色:

1. 生成 xray-core 客户端配置,启动并守护 xray 子进程;

2. 在 xray 本地入站通过真实探测之后,把 DSH 进程的全局 HTTP(S) 出口接到该入站;

3. 可选:通过零依赖 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,无需构建;除控制面外无运行时依赖。

1. 安装:npm i @twinsearth/dsh-2xray 后由插件管理器启用;或在 DSH 插件页选择「从目录安装」,指向 E:\GPT\dsh-2xray。

2. 把 xray 可执行文件放到下面任一位置(按解析优先级):

  • 插件自己的 bin/xray(.exe)(随 bundle 分发时用;见 [bin/README.md](bin/README.md) 的 MPL-2.0 说明)
  • 配置 binary.path
  • 环境变量 DSH_XRAY_BIN
  • <DSH_HOME>\xray-proxy\bin\xray.exe(DSH_HOME 默认 %USERPROFILE%\.dsh)
  • 放进 PATH

3. 编辑 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

…(truncated)

Rendered from the README published on npm. Formatted only, not edited.

Versions

Published on npm::29,最后更新 2026-11-04

Links

GitHub · Homepage · npm

Same category