Bundle
dsh-plugin-proxy-env
DeepSeek Harness plugin: inject proxy environment variables at startup and self-check the whole proxy chain
- Source
- looeton
- License
- MIT
- Updated
- Updated 8 days ago
Readme
# dsh-plugin-proxy-env
> DSH 插件:启动时自动注入代理环境变量,并自检整条代理链路。
> A DeepSeek Harness plugin that injects proxy environment variables at startup and self-checks the whole proxy chain.
[](https://github.com/looeton/dsh-plugin-proxy-env/actions/workflows/ci.yml)
[](LICENSE)
[](#中文)
[](#中文)
[中文](#中文) · [English](#english)
---
## 中文
### 这是什么
一个 DSH 插件,做两件事:
1. **启动时注入代理变量**——把 `HTTP_PROXY` / `HTTPS_PROXY` / `NODE_USE_ENV_PROXY` / `NO_PROXY` 写进 harness 主进程的环境,之后**所有子进程**(shell、git、npm、python)自动继承,不用再 `setx` + 重启。
2. **自检**——从本地端口一路查到端到端请求,把结论写进日志和报告文件;同时注册一个 `proxy_env_check` 工具,随时能再查一次。
### 为什么这样能生效
DSH 在**派生子进程时**从 `process.env` 构造子环境(`dsh-subprocess` 的 `scrubbedParentEnv()`,它只剔除名字含 `KEY/PASSWORD/SECRET/TOKEN` 的和 `DSH_*` 的变量,**代理变量是刻意保留的**)。所以插件在加载阶段写一次 `process.env`,后面每一次 spawn 都带着它——比手工 `setx` 更即时,也不依赖启动方式。
### 注入哪些变量
| 变量 | 值 | 作用 |
|---|---|---|
| `HTTP_PROXY` / `HTTPS_PROXY` | 解析到的代理地址 | curl / git / python / Go 等自动读取 |
| `NODE_USE_ENV_PROXY` | `1` | **子** Node 进程启动时即让内置 `fetch` 读取上面的变量 |
| `NO_PROXY` | 默认 `localhost,127.0.0.1,::1,api.deepseek.com` | 回环必须排除,否则本地 GUI 请求会被塞进代理;模型 API 排除后,代理挂掉也不影响对话 |
> 已存在的同名变量默认**不覆盖**(`override: true` 可强制)。
### 自检做什么
按「由近到远」的顺序,先报最上游的断点:
1. 代理端口是否监听
2. 端口是否应答 `CONNECT`(手工建隧道,不依赖 undici)
3. DNS 是否返回真实地址(识别 `198.18.0.0/15` 与 `fdfe:dcba:9876::/64` 的 fake-ip)
4. 直连探针 + 走代理探针(TLS 隧道)
5. 汇总结论:`PASS` / `WARN` / `FAIL` + 一条修复建议
结果写入 `$DSH_HOME/proxy-env/report.json`,agent 可以直接读取,不必调用工具。
### 安装
**方式一:DSH Desktop 插件管理器**(推荐)
设置 → 插件 → 安装,填入 `dsh-plugin-proxy-env`(发布到 npm 后可用)。
**方式二:手动(从本仓库安装)**
```powershell
# 1) 进入当前 profile 目录
cd "$env:DSH_HOME\profiles\desktop"
# 2) 安装(本地路径、git 或 npm 均可)
pnpm add file:D:/path/to/dsh-plugin-proxy-env
# 或 pnpm add github:looeton/dsh-plugin-proxy-env
# 3) 把包名加进 package.json 的 dsh.profile.bundles
# "dsh": { "profile": { "bundles": [ ..., "dsh-plugin-proxy-env" ] } }
# 4) 重启 DSH Desktop
```
**方式三:先本地验证,不装进 profile**
```powershell
node src/cli.mjs # 独立自检,跟插件走同一套代码
node --test tests/*.test.mjs # 52 个单元测试
```
### 配置项
通过 patch 行或 profile 的 `cordis.patch.yml` 传入:
```yaml
- id: proxy-env
config:
proxyUrl: http://127.0.0.1:7897 # 留空则自动探测
noProxy: localhost,127.0.0.1,::1,api.deepseek.com
selfCheck: true
tool: true
```
| 键 | 默认 | 说明 |
|---|---|---|
| `enable` | `true` | 总开关 |
| `proxyUrl` | 自动 | 完整代理地址;留空则按 `port` → Windows 系统代理 → 已导出的环境变量顺序解析 |
| `host` | `127.0.0.1` | 本地代理主机 |
| `port` | `0` | 直接指定端口 |
| `noProxy` | 见上 | `NO_PROXY` 内容 |
| `override` | `false` | 是否覆盖已存在的同名变量 |
| `patchGlobalDispatcher` | `true` | 动态 import `undici`,把全局 dispatcher 换成 `EnvHttpProxyAgent`,让**运行时**的 Node 请求也走代理 |
| `selfCheck` | `true` | 启动时自检 |
| `writeReport` | `true` | 写 `$DSH_HOME/proxy-env/report.json` |
| `tool` | `true` | 注册 `proxy_env_check` 工具 |
| `probeUrl` | `https://www.google.com` | 只有走代理才通的探针 |
| `directProbeUrl` | `https://example.com` | 直连探针 |
| `dnsHost` | `example.com` | fake-ip 检测域名 |
| `timeoutMs` | `15000` | 单个探针超时 |
### 提供的工具
| 工具 | 参数 | 返回 |
|---|---|---|
| `proxy_env_check` | `probe_url`、`skip_network` | 一行结论 + 完整链路文本 |
### 局限
- **它管的是进程环境,不是 `web_fetch`**。DSH 的 `web_fetch` 用固定 IP 的自定义 undici `Agent`(防 SSRF / DNS rebinding),刻意绕过全局 dispatcher,所以即使代理正常它也不会走代理——这是设计,不是本插件的缺陷。被墙站点请用能走云端的抓取工具,或让 agent 用 shell 里的 `node` / `python` 抓。
- **注册表读取依赖 `reg.exe`**,且用文件重定向而非管道(受限令牌沙箱里管道会 EPERM)。读不到时自动回退到已导出的 `HTTPS_PROXY` / `HTTP_PROXY`。
- **非 Windows** 只走配置和环境变量,不读系统代理。
- **`patchGlobalDispatcher` 是运行时切换**:它需要能解析到 `undici`,且版本要有 `EnvHttpProxyAgent`(唯一会遵守 `NO_PROXY` 的实现);不满足时跳过并在日志里说明。
### 相关项目
- **[clash-verge-system-proxy](https://github.com/looeton/clash-verge-system-proxy)** —— 手工版完整指南(关 TUN、开系统代理、写变量、排错手册)
- **[proxy-doctor](https://github.com/looeton/proxy-doctor)** —— 独立的 PowerShell 诊断工具,不装插件也能用
---
## English
### What this is
A DSH plugin that does two things:
1. **Injects proxy variables at startup** — writes `HTTP_PROXY` / `HTTPS_PROXY` / `NODE_USE_ENV_PROXY` / `NO_PROXY` into the harness host process, so **every child process** (shell, git, npm, python) inherits them. No `setx`, no restart dance.
2. **Self-checks the chain** — walks from the local port to a real request, logs a verdict, writes a report file, and registers a `proxy_env_check` tool for on-demand re-runs.
### Why this works
DSH builds each child environment from `process.env` at spawn time (`scrubbedParentEnv()` in `dsh-subprocess` strips credential-shaped names and `DSH_*`, but **keeps proxy variables on purpose**). Writing `process.env` once during plugin load therefore covers every later spawn — immediately, and independent of how the app was launched.
### What it injects
| Variable | Value | Purpose |
|---|---|---|
| `HTTP_PROXY` / `HTTPS_PROXY` | resolved proxy URL | read by curl, git, python, Go, … |
| `NODE_USE_ENV_PROXY` | `1` | makes **child** Node processes honour the two above from bootstrap |
| `NO_PROXY` | `localhost,127.0.0.1,::1,api.deepseek.com` | loopback must be excluded or local GUI requests go through the proxy; the model API is excluded so a dead proxy cannot break chat |
> Existing values are left alone unless `override: true`.
### What the self-check does
Nearest-first, so the report always names the earliest broken link:
1. is the proxy port listening
2. does it answer `CONNECT` (hand-built tunnel, no undici needed)
3. does DNS return real addresses (detects `198.18.0.0/15` and `fdfe:dcba:9876::/64` fake-ip)
4. direct probe + through-proxy probe (TLS tunnel)
5. one verdict — `PASS` / `WARN` / `FAIL` — plus one fix
The report lands in `$DSH_HOME/proxy-env/report.json`, readable by the agent without a tool call.
### Install
**Option 1 — DSH Desktop plugin manager** (recommended): Settings → Plugins → Install, then `dsh-plugin-proxy-env` (available once published to npm).
**Option 2 — manual, from this repository**
```powershell
cd "$env:DSH_HOME\profiles\desktop"
pnpm add file:D:/path/to/dsh-plugin-proxy-env # or github:looeton/dsh-plugin-proxy-env
# add "dsh-plugin-proxy-env" to dsh.profile.bundles in package.json
# restart DSH Desktop
```
**Option 3 — verify locally without installing**
```powershell
node src/cli.mjs # standalone self-check, same code path
node --test tests/*.test.mjs # 52 unit tests
```
### Configuration
```yaml
- id: proxy-env
config:
proxyUrl: http://127.0.0.1:7897 # empty = auto-detect
noProxy: localhost,127.0.0.1,::1,api.deepseek.com
selfCheck: true
tool: true
```
| Key | Default | Purpose |
|---|---|---|
| `enable` | `true` | master switch |
| `proxyUrl` | auto | full proxy URL; when empty the order is `port` → Windows system proxy → exported env var |
| `host` | `127.0.0.1` | local proxy host |
| `port` | `0` | explicit port |
| `noProxy` | see above | `NO_PROXY` contents |
| `override` | `false` | overwrite existing variables |
| `patchGlobalDispatcher` | `true` | dynamically import `undici` and switch the global dispatcher to `EnvHttpProxyAgent`, so runtime Node requests use the proxy too |
| `selfCheck` | `true` | run the check at startup |
| `writeReport` | `true` | write `$DSH_HOME/proxy-env/report.json` |
| `tool` | `true` | register the `proxy_env_check` tool |
| `probeUrl` | `https://www.google.com` | only reachable through the proxy |
| `directProbeUrl` | `https://example.com` | reachable directly |
| `dnsHost` | `example.com` | host used for the fake-ip check |
| `timeoutMs` | `15000` | per-probe timeout |
### The tool it registers
| Tool | Arguments | Returns |
|---|---|---|
| `proxy_env_check` | `probe_url`, `skip_network` | one-line verdict plus the full chain report |
### Limitations
- **It covers process environment, not `web_fetch`.** DSH's `web_fetch` pins connections with a custom undici `Agent` (SSRF / DNS-rebinding defence) and bypasses the global dispatcher by design, so it will not use the proxy even when everything else does. For blocked sites use a cloud-based page reader, or have the agent fetch with `node` / `python` from a shell.
- **Registry reading shells out to `reg.exe`**, with stdout redirected to a temp file rather than a pipe (pipes fail with EPERM inside restricted-token sandboxes). When unreadable it falls back to an exported `HTTPS_PROXY` / `HTTP_PROXY`.
- **Non-Windows** hosts use configuration and environment variables only.
- **`patchGlobalDispatcher` is a runtime switch**: it needs `undici` to resolve and a version that ships `EnvHttpProxyAgent` (the only variant that honours `NO_PROXY`). Otherwise it is skipped and logged.
### Related projects
- **[clash-verge-system-proxy](https://github.com/looeton/clash-verge-system-proxy)** — the manual companion guide (TUN off, system proxy on, variables, troubleshooting)
- **[proxy-doctor](https://github.com/looeton/proxy-doctor)** — standalone PowerShell diagnosis, no plugin needed
---
## Development
```bash
node --check src/*.mjs # or: npm run check
npm test # or: node --test tests/*.test.mjs
node src/cli.mjs # self-check against the current machine
```
## License
[MIT](LICENSE)
Install
dsh plugin --profile web add github:looeton/dsh-plugin-proxy-env
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-plugin-proxy-env from the hub
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.