dsh-proxy-routing 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,57 @@
1
+ # Changelog
2
+
3
+ All notable changes to `dsh-proxy-routing` are documented here.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project uses [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [0.4.0] - 2026-08-21
8
+
9
+ ### Changed
10
+
11
+ - **Breaking:** migrated persistence from the private `proxy-agent.json` file to the official `ctx.settings` `proxy-routing` namespace in `$DSH_HOME/settings.yaml`.
12
+ - The official settings-file provider now owns YAML parsing, atomic writes, locking, namespace preservation, and external hot reload.
13
+ - Removed automatic legacy-file reads and migration; existing `proxy-agent.json` files are ignored and must be migrated manually.
14
+ - Kept the v2 canonical route shape, direct Agent default, per-execution routing, approval tiers, credential redaction, and serialized runtime transaction semantics.
15
+ - Added settings-backed compensating rollback when runtime participant preparation or commit fails after a settings write.
16
+ - Removed the invalid `./client` package export that pointed to a missing `lib/client.js` entry.
17
+
18
+ ### Added
19
+
20
+ - Registered the `proxy-routing` settings schema with secret roles for profile credentials.
21
+ - Added YAML-aware CLI probe loading from the official settings document.
22
+ - Added settings namespace, external update, no-legacy-file, and compensating transaction coverage.
23
+ - Restructured the bilingual README into concise installation, configuration, and Agent usage guidance.
24
+ - Clarified that the plugin resolves a default direct-routing configuration for out-of-the-box use, while proxy endpoint values remain user-configured.
25
+ - Documented the v2 YAML settings schema and the purpose of each field.
26
+ - Clarified that the plugin connects to an existing HTTP or SOCKS5 proxy and does not provide proxy infrastructure.
27
+ - The approval gate for `net_proxy_enable` / `net_proxy_disable` respects permission tiers: Full Access (`danger-full-access`) skips the user prompt, while lower tiers (`read-only` / `workspace-write`) require human approval.
28
+ - Documented concrete pain-point examples (`gh`/GitHub access, `npm`/`pnpm`/`cargo` dependency installs, `docker pull`) and permission tier behavior in both READMEs.
29
+ - Added the Chinese README counterpart and bilingual consistency record.
30
+ - Submitted the plugin to the [awesome-dsh-plugin catalog](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/pull/1299).
31
+
32
+ ## [Unreleased]
33
+
34
+ ## [0.4.1] - 2026-08-30
35
+
36
+ ### Added
37
+
38
+ - Added a dynamic `proxy-routing:onboarding` system-prompt section that tells the Agent when the default endpoint is absent, uses Full Access-only discovery when permitted, asks restricted sessions for an existing proxy `protocol`/`host`/`port`, and recommends probe-before-enable without guessing an address.
39
+ - Added `net_proxy_discover`, a bounded read-only scan of proxy environment variables and common loopback ports with credential-redacted results.
40
+ - Added the Web `Settings → General → Network Proxy` row, backed by `ctx.settingsScope` and the official `settings.general.item` slot.
41
+ - Added a loopback `@deepseek-ai/dsh-client-connection` RPC channel for browser-side discovery, endpoint probing, and atomic endpoint/Agent-route mutation; it uses settings revision CAS and does not expose or rebuild proxy credentials.
42
+
43
+ ### Changed
44
+
45
+ - Clarified the install restart requirement, official Cordis host/module HMR, current Web profile HMR disablement, active client bundle HMR, and settings YAML hot reload as separate mechanisms.
46
+ - Exposed the plugin-owned `proxy-routing` namespace through the Web settings allowlist in DSH host API proxy.
47
+ - Replaced the stale documentation claim that there is no Web Network Proxy settings page.
48
+ - Updated user installation instructions for DSH profiles and npm registry installation.
49
+
50
+ ### Fixed
51
+
52
+ - Successful probe results no longer contain `error: undefined`, so DSH lossless JSON tool-result validation accepts successful probes.
53
+ - `net_proxy_status(verify=true)` now projects probe output to its declared schema without leaking the probe target field.
54
+
55
+ [Unreleased]: https://github.com/chenjiyan2001/dsh-proxy-routing/compare/v0.4.1...HEAD
56
+ [0.4.1]: https://github.com/chenjiyan2001/dsh-proxy-routing/releases/tag/v0.4.1
57
+ [0.4.0]: https://github.com/chenjiyan2001/dsh-proxy-routing/releases/tag/v0.4.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,4 @@
1
+ # Bilingual-pair consistency record: re-record after editing either README.
2
+ # Both language files carry equal authority.
3
+ README.md: acf6677ff1aa0354405d1070d4a57b131be194da
4
+ README.zh.md: 27c961f12df6fae907b1c35869b27faf394abd71
package/README.md ADDED
@@ -0,0 +1,193 @@
1
+ # dsh-proxy-routing
2
+
3
+ English | [中文](README.zh.md)
4
+
5
+ A DeepSeek Harness egress-routing plugin that connects to an **already running** HTTP or SOCKS5 proxy in the environment, routing shell commands and LLM provider traffic per execution without mutating the host process environment.
6
+
7
+ This plugin does not provide proxy functionality. It does not create a proxy service, manage nodes or subscriptions, or implement split-routing rules. An accessible HTTP or SOCKS5 proxy endpoint must already exist before using it.
8
+
9
+ ## Overview
10
+
11
+ - Connects to an existing local HTTP (including CONNECT) or SOCKS5 proxy.
12
+ - Keeps direct connection as the default. `NO_PROXY` excludes loopback addresses and `api.deepseek.com` by default.
13
+ - Routes foreground and background Bash/PowerShell subprocesses, including nested `git`, `curl`, `npm`, and `pnpm` commands.
14
+ - Applies provider-specific routes to LLM streams while isolating concurrent direct and proxied requests.
15
+ - Persists configuration through the official `proxy-routing` settings namespace in `$DSH_HOME/settings.yaml` and applies changes without restarting DSH.
16
+ - Adds a compact `Settings → General → Network Proxy` row when the Web client is available; it edits the official namespace, tests a supplied endpoint, and shows discovered candidates without enabling them.
17
+ - In Full Access sessions, `net_proxy_discover` can proactively test proxy environment variables and a bounded set of loopback ports; restricted sessions fail closed and ask the user for `protocol`/`host`/`port`.
18
+ - Requires human approval for enabling or disabling a proxy route (except under the Full Access permission tier).
19
+
20
+ ## Pain Points and Use Cases
21
+
22
+ ### Concrete examples
23
+
24
+ | Scenario | Direct-connection pain point | What this plugin does |
25
+ |---|---|---|
26
+ | `gh repo clone`, `git clone` from GitHub | `Failed to connect to github.com port 443: Connection timed out` | After enabling, every `git`/`gh` subprocess the Agent spawns inherits `HTTP(S)_PROXY`; no `~/.gitconfig` or `git config http.proxy` changes |
27
+ | `curl`/`wget` downloads of external files | Downloads hang or drop (`connect timed out`) | Command-line subprocesses started by DSH inherit the injected proxy environment for both foreground and background runs |
28
+ | Agent installs dependencies with `npm`/`pnpm`/`cargo` | `ETIMEDOUT`, `ESOCKETTIMEDOUT`, interrupted downloads | Dependency-install subprocesses go through the proxy automatically; prefer package mirrors when available |
29
+ | LLM provider API domains blocked or rate-limited | Interrupted streaming, provider unreachable | Configure a per-provider proxy route without touching direct traffic |
30
+ | `docker pull` image pulls | `dial tcp ... i/o timeout`, `net/http: TLS handshake timeout` | **Not applicable**: the Docker daemon performs image pulls, not this plugin; configure the proxy or a registry mirror in Docker Desktop / the daemon |
31
+
32
+ ### Not applicable
33
+
34
+ - Docker daemon image pulls and BuildKit-side network traffic.
35
+ - Programs that ignore proxy environment variables, such as browsers and WinHTTP/WinINET applications.
36
+ - Providing a proxy service: this plugin only connects to a proxy already running in the environment.
37
+
38
+ ## Install
39
+
40
+ Version `0.4.1` is published to npm and is also available as a prebuilt tarball in the [GitHub Release](https://github.com/chenjiyan2001/dsh-proxy-routing/releases/tag/v0.4.1). Choose the installation path that matches how you run the plugin.
41
+
42
+ ### Install through DSH (recommended)
43
+
44
+ Install the plugin into the `web` profile:
45
+
46
+ ```bash
47
+ dsh plugin --profile web add dsh-proxy-routing@0.4.1
48
+ ```
49
+
50
+ Replace `web` with another profile name when needed. This command updates the profile's package manifest and bundle list so DSH can load the plugin. Restart a running DSH process once after installation so it composes the new entry and registers the `net_proxy_*` tools. The DSH plugin command requires `pnpm` to be available on `PATH`.
51
+
52
+ ### Install with npm
53
+
54
+ Install the published package into an npm-managed project:
55
+
56
+ ```bash
57
+ npm install dsh-proxy-routing@0.4.1
58
+ ```
59
+
60
+ This makes the package available as a Node dependency. Installing it with npm alone does not add the plugin to a DSH profile or its bundle list; for a normal DSH installation, use the DSH command above. The npm path is for a host application or custom profile that manages the package itself.
61
+
62
+ ### Install from a local checkout (development only)
63
+
64
+ Add the plugin directory to the profile's `cordis.patch.yml` using an absolute path:
65
+
66
+ ```yaml
67
+ - insert:
68
+ - id: proxy-routing
69
+ name: '/path/to/dsh-proxy-routing'
70
+ ```
71
+
72
+ ### First-use setup
73
+
74
+ Installation intentionally does not enable a proxy. The plugin only connects to an HTTP or SOCKS5 proxy that is already running; it never starts one or silently guesses an endpoint.
75
+
76
+ - In a Full Access Agent session, call `net_proxy_discover`. It checks proxy environment variables and a bounded set of common loopback ports. Successful candidates are reported only; confirm the candidate's purpose before enabling it.
77
+ - In a restricted Agent session, discovery is refused by design. Ask the user for an existing proxy's `protocol`, `host`, and `port`, then call `net_proxy_probe`.
78
+ - In Web, use `Settings → General → Network Proxy`. `Discover local proxy` and `Test connection` are read-only diagnostics; selecting or saving a candidate does not enable routing.
79
+ - After a successful probe, request `net_proxy_enable` with the endpoint. Full Access applies it without a prompt; lower permission tiers require human approval.
80
+ - Use `net_proxy_status` to verify the effective route and `net_proxy_disable` to return the Agent to direct mode.
81
+
82
+ For a manual setup, add the endpoint and a profile route to the `proxy-routing` YAML shown below. The Web diagnostics use the official loopback Connection RPC and never expose proxy credentials.
83
+
84
+ ### Restart and hot reload
85
+
86
+ There are four separate mechanisms:
87
+
88
+ - **Install or remove a plugin:** `dsh plugin ... add/remove` edits the profile files. The already-running process does not rescan those files or rebuild its Cordis loader tree, so a restart is required for the first registration or removal.
89
+ - **Plugin host code:** official Cordis module HMR supports unloading and re-applying plugin code when the HMR service is enabled, its `root` includes the source, `timer` and loader internals are available, and the development host is started with the required Node internals. The normal Web profile currently overrides the shared HMR row with `disabled: true` because that Web reload lifecycle is still marked untested; this is a Web composition decision, not a general plugin limitation.
90
+ - **Plugin client code:** the Web profile keeps `@deepseek-ai/dsh-client-hmr` mounted. With `pnpm run dev:web` running from the same DSH checkout, changes to this package's `lib/client.js` can be delivered to the browser module graph without a page refresh. A normal installed package without the watcher still requires rebuilding the client artifact and refreshing.
91
+ - **Settings YAML:** the official settings-file provider watches `$DSH_HOME/settings.yaml`. Once the plugin is loaded, changes to the `proxy-routing` section are applied at runtime without restarting DSH.
92
+
93
+ ## Configuration
94
+
95
+ ### Default state
96
+
97
+ A new v2 settings namespace resolves to a default structure that works out of the box in direct mode:
98
+
99
+ - The default Agent route is direct: `bindings.agent.kind = "direct"`.
100
+ - The `default` profile includes default `noProxy` and `timeout` values, but no real proxy endpoint.
101
+ - `noProxy` defaults to `127.0.0.1`, `localhost`, `::1`, and `api.deepseek.com`.
102
+ - `timeout` defaults to `60000` milliseconds.
103
+ - The user must provide `protocol`, `host`, and `port` for a network proxy, pointing to a proxy service already running in the environment.
104
+ - Existing `proxy-agent.json` files are ignored. This release does not automatically migrate the old private-file format; copy values into the YAML namespace manually if needed.
105
+
106
+ The plugin therefore works out of the box for direct connections after installation. A user only needs to configure a proxy endpoint when a proxied egress is required.
107
+
108
+ ### Configuration schema
109
+
110
+ The official DSH settings provider stores this namespace in `$DSH_HOME/settings.yaml` (falling back to `~/.dsh/settings.yaml` when `DSH_HOME` is unset). Other settings namespaces in the same document are preserved. The following YAML is the v2 canonical shape:
111
+
112
+ ```yaml
113
+ proxy-routing:
114
+ version: 2
115
+ profiles:
116
+ - id: default
117
+ protocol: http
118
+ host: 127.0.0.1
119
+ port: 7897
120
+ username: ""
121
+ password: ""
122
+ noProxy: [127.0.0.1, localhost, ::1, api.deepseek.com]
123
+ timeout: 60000
124
+ bindings:
125
+ agent:
126
+ kind: direct
127
+ # A profile route also requires: profileId: default
128
+ providers: []
129
+ gateway: null
130
+ gatewayPurposes: []
131
+ gateway:
132
+ enabled: false
133
+ port: 17890
134
+ dedicatedPurposePorts: false
135
+ purposes: []
136
+ ```
137
+
138
+ `protocol`, `host`, and `port` must point to a proxy service that is already running in the environment. The namespace is registered through `ctx.settings`; the official settings-file provider owns YAML parsing, atomic writes, locking, and hot reload. The plugin does not read, write, watch, or migrate `proxy-agent.json`.
139
+
140
+ Fixed guidance for an unconfigured endpoint:
141
+
142
+ > Proxy endpoint is not configured: in Full Access call `net_proxy_discover`; in a restricted session ask the user for `protocol`/`host`/`port`, call `net_proxy_probe`, then request `net_proxy_enable`.
143
+
144
+ ## Agent Installation Guide
145
+
146
+ The Agent only discovers and requests use of an existing proxy; it does not start a proxy service. Recommended flow:
147
+
148
+ 1. In Full Access, call `net_proxy_discover`; it checks proxy environment variables and a bounded set of loopback candidates. In a restricted session, do not scan; ask the user for `protocol`, `host`, and `port`.
149
+ 2. When an external request, fetch, or download fails, call `net_proxy_status` and check whether `default` is configured.
150
+ 3. Call `net_proxy_probe` to test the current or temporary endpoint. Do not invent an address when no endpoint is configured.
151
+ 4. Request `net_proxy_enable` with the endpoint details. Under Full Access it takes effect immediately; under lower tiers, wait for human approval before retrying.
152
+ 5. Request `net_proxy_disable` when direct routing should be restored. Explicit provider overrides are retained.
153
+
154
+ ### Permission tiers
155
+
156
+ | Permission tier | `net_proxy_enable` / `net_proxy_disable` behavior |
157
+ |---|---|
158
+ | Full Access (`danger-full-access`) | No user prompt; takes effect immediately |
159
+ | Lower tiers (`read-only` / `workspace-write`, etc.) | Requires human approval before taking effect |
160
+
161
+ The read-only tools `net_proxy_status` / `net_proxy_probe` / `net_proxy_discover` never require approval at any tier.
162
+
163
+ Available tools:
164
+
165
+ | Tool | Approval | Use |
166
+ |---|---|---|
167
+ | `net_proxy_status` | Read-only | Inspect revision, endpoint configuration, effective Agent route, provider overrides, fetch ownership, and persistent-shell generation. `verify` optionally probes connectivity. |
168
+ | `net_proxy_probe` | Read-only | Test the current or supplied endpoint without changing configuration. |
169
+ | `net_proxy_discover` | Full Access only | Probe environment proxy variables and a bounded set of loopback candidates; reports successful candidates only and never enables or persists a route. |
170
+ | `net_proxy_enable` | Required (except Full Access) | Point the Agent route at `default`; may update endpoint fields and persist the change. |
171
+ | `net_proxy_disable` | Required (except Full Access) | Restore the Agent route to direct; provider overrides and endpoint configuration remain. |
172
+
173
+ ## Develop and test
174
+
175
+ ```bash
176
+ pnpm install
177
+ pnpm test
178
+ pnpm run build:client
179
+ pnpm run probe
180
+ ```
181
+
182
+ `pnpm run build:client` emits `lib/client.js`, the Web module-loader artifact. For client HMR, run `pnpm run dev:web` from the same DSH checkout while developing; otherwise rebuild the client artifact and refresh the injected DSH Web page.
183
+
184
+ Tests cover HTTP/CONNECT/SOCKS5 transport, timeouts, backpressure, `NO_PROXY`, redirects, probing and bounded discovery, approval gates, settings-schema validation and YAML hot reload, loopback RPC validation, compensating settings transactions, fetch/LLM/shell isolation, HMR/dispose idempotency, and real subprocess inheritance.
185
+
186
+ ## References
187
+
188
+ - Transport reference: [mafeis/dsh-net-proxy](https://github.com/mafeis/dsh-net-proxy) (MIT).
189
+ - Configuration naming reference: [@cordisjs/plugin-proxy-agent](https://www.npmjs.com/package/@cordisjs/plugin-proxy-agent).
190
+
191
+ ## License
192
+
193
+ MIT
package/README.zh.md ADDED
@@ -0,0 +1,193 @@
1
+ # dsh-proxy-routing
2
+
3
+ [English](README.md) | 中文
4
+
5
+ DeepSeek Harness 网络出口路由插件:按需接入**环境中已经运行的** HTTP 或 SOCKS5 代理,按每次执行路由 shell 命令和 LLM provider 流量,不修改宿主进程环境。
6
+
7
+ 本插件不提供网络代理功能,不创建代理服务,不管理节点、订阅或分流规则。使用前,运行环境中必须已经有可访问的 HTTP 或 SOCKS5 代理端点。
8
+
9
+ ## 功能概览
10
+
11
+ - 接入本机已有的 HTTP(包括 CONNECT)或 SOCKS5 代理。
12
+ - 默认保持直连;`NO_PROXY` 默认排除回环地址和 `api.deepseek.com`。
13
+ - 覆盖 Bash/PowerShell 的前台、后台子进程,包括嵌套的 `git`、`curl`、`npm`、`pnpm`。
14
+ - 支持按 provider 路由 LLM 流,并隔离并发的直连流与代理流。
15
+ - 配置通过官方 `proxy-routing` settings namespace 持久化到 `$DSH_HOME/settings.yaml`,修改后无需重启 DSH 即可生效。
16
+ - Web 客户端可在现有 `Settings → General → Network Proxy` 行中编辑 namespace、测试端点并选择发现候选;诊断不会自动启用代理。
17
+ - Full Access Agent 会话可通过 `net_proxy_discover` 主动检查代理环境变量和有限的本机端口;受限会话会 fail closed,改为向用户询问 `protocol`/`host`/`port`。
18
+ - 启用或停用代理路由属于网络行为变更,需要人类审批(Full Access 权限等级除外)。
19
+
20
+ ## 痛点与适用场景
21
+
22
+ ### 典型案例
23
+
24
+ | 场景 | 直连时的痛点 | 本插件的作用 |
25
+ |---|---|---|
26
+ | `gh repo clone`、`git clone` 拉 GitHub 仓库 | `Failed to connect to github.com port 443: Connection timed out` | 启用代理后,Agent 每次执行的 `git`/`gh` 子进程自动携带 `HTTP(S)_PROXY`,无需改 `~/.gitconfig` 或 `git config http.proxy` |
27
+ | `curl`/`wget` 下载外网文件 | 下载卡死或中断(`connect timed out`) | 由 DSH 启动的命令行子进程继承注入的代理环境,前台、后台执行都生效 |
28
+ | Agent 用 `npm`/`pnpm`/`cargo` 装依赖 | `ETIMEDOUT`、`ESOCKETTIMEDOUT`、依赖下载中断 | 依赖安装子进程自动走代理;已有镜像源时仍建议优先配置镜像 |
29
+ | LLM provider API 域名受限或被限流 | 对话流中断、provider 不可达 | 按 provider 配置代理路由,其余直连流量不受影响 |
30
+ | `docker pull` 拉镜像 | `dial tcp ... i/o timeout`、`net/http: TLS handshake timeout` | **不适用**:镜像由 Docker daemon 拉取,不经本插件路由;需在 Docker Desktop / daemon 配置代理或 registry mirror |
31
+
32
+ ### 不适用场景
33
+
34
+ - Docker daemon 的镜像拉取与构建(`docker pull`、BuildKit 侧网络)。
35
+ - 浏览器、WinHTTP/WinINET 应用等不读环境变量代理的程序。
36
+ - 由插件自行搭建或提供代理服务:本插件只接入环境中已经运行的代理。
37
+
38
+ ## 安装
39
+
40
+ 版本 `0.4.1` 已发布到 npm,也可从 [GitHub Release](https://github.com/chenjiyan2001/dsh-proxy-routing/releases/tag/v0.4.1) 获取预构建 tarball。请根据你的使用方式选择下面的安装路径。
41
+
42
+ ### 通过 DSH 安装(推荐)
43
+
44
+ 将插件安装到 `web` profile:
45
+
46
+ ```bash
47
+ dsh plugin --profile web add dsh-proxy-routing@0.4.1
48
+ ```
49
+
50
+ 如使用其他 profile,请将 `web` 替换为对应名称。该命令会更新 profile 的 package manifest 和 bundle 列表,使 DSH 能够加载插件。已经运行的 DSH 在安装后需要重启一次,才能重新组合 loader 并注册 `net_proxy_*` 工具。DSH 插件命令要求 `PATH` 中可用 `pnpm`。
51
+
52
+ ### 通过 npm 安装
53
+
54
+ 将已发布包安装到 npm 管理的项目中:
55
+
56
+ ```bash
57
+ npm install dsh-proxy-routing@0.4.1
58
+ ```
59
+
60
+ 这会把插件作为 Node 依赖安装。单独通过 npm 安装不会自动把插件加入 DSH profile 或 bundle 列表;普通 DSH 用户应使用上面的 DSH 安装命令。只有在宿主应用或自定义 profile 自行管理依赖时,才使用 npm 安装路径。
61
+
62
+ ### 从本地 checkout 安装(仅开发)
63
+
64
+ 将插件目录以绝对路径加入 profile 的 `cordis.patch.yml`:
65
+
66
+ ```yaml
67
+ - insert:
68
+ - id: proxy-routing
69
+ name: '/path/to/dsh-proxy-routing'
70
+ ```
71
+
72
+ ### 首次使用配置
73
+
74
+ 安装时不会自动启用代理,这是有意设计:插件只接入已经运行的 HTTP 或 SOCKS5 代理,不启动代理服务,也不会静默猜测端点。
75
+
76
+ - Full Access Agent 会话可调用 `net_proxy_discover`。它检查代理环境变量和有限的常见本机端口,只返回探测成功的候选;启用前仍需确认候选用途。
77
+ - 受限 Agent 会话会拒绝主动扫描。请先向用户询问已经运行的代理 `protocol`、`host`、`port`,再调用 `net_proxy_probe`。
78
+ - Web 端使用 `Settings → General → Network Proxy`。`发现本机代理` 和 `测试连接` 都是只读诊断;选择或保存候选不会自动启用路由。
79
+ - 探测成功后,用同一端点请求 `net_proxy_enable`。Full Access 等级会直接生效,较低权限等级需要人类批准。
80
+ - 使用 `net_proxy_status` 检查当前生效路由;需要恢复直连时调用 `net_proxy_disable`。
81
+
82
+ 也可以手动编辑下方的 `proxy-routing` YAML,写入代理端点和 profile 路由。Web 诊断通过官方 loopback Connection RPC 提供,不会向浏览器返回代理凭据。
83
+
84
+ ### 重启与热更新边界
85
+
86
+ 这里有四套不同机制:
87
+
88
+ - **安装或移除插件**:`dsh plugin ... add/remove` 修改的是 profile 文件。已经运行的进程不会重新扫描这些文件,也不会重建 Cordis loader tree,所以首次注册或移除后必须重启。
89
+ - **插件 Host 代码**:官方 Cordis module HMR 支持卸载旧 fiber、清理 effects/registrations、重新导入并执行插件。它要求启用 HMR service、`root` 覆盖源码、`timer` 和 loader internals 可用,并由带必要 Node internals 的开发宿主运行。普通 Web profile 当前明确将共享 HMR 行设为 `disabled: true`,原因是 Web reload lifecycle 仍标记为未测试;这是 Web composition 的部署决定,不是插件一般不能 HMR。
90
+ - **插件 Client 代码**:Web profile 仍挂载 `@deepseek-ai/dsh-client-hmr`。从同一 DSH checkout 运行 `pnpm run dev:web` 时,本插件 `lib/client.js` 的变更可以通过 browser module graph 热更新而无需刷新页面;没有 watcher 时仍需重建 client artifact 并刷新。
91
+ - **settings YAML**:官方 settings-file provider 会监听 `$DSH_HOME/settings.yaml`。插件成功加载后,修改 `proxy-routing` section 可以在运行时应用,不需要重启 DSH。
92
+
93
+ ## 配置
94
+
95
+ ### 默认状态
96
+
97
+ 全新的 v2 settings namespace 会解析为默认配置结构,能够在不配置代理时直接以直连方式工作:
98
+
99
+ - Agent 默认路由为直连:`bindings.agent.kind = "direct"`。
100
+ - `default` profile 默认包含 `noProxy` 和 `timeout` 配置,但不包含实际代理端点。
101
+ - `noProxy` 默认排除 `127.0.0.1`、`localhost`、`::1` 和 `api.deepseek.com`。
102
+ - `timeout` 默认是 `60000` 毫秒。
103
+ - 网络代理的 `protocol`、`host`、`port` 必须由用户提供,并指向环境中已经运行的代理服务。
104
+ - 已存在的 `proxy-agent.json` 会被忽略。本版本不会自动迁移旧的私有文件格式;如需保留配置,请手动复制到 YAML namespace。
105
+
106
+ 因此,插件安装后即可开箱使用直连能力;只有需要代理出口时,才需要用户配置代理端点。
107
+
108
+ ### 配置 Schema
109
+
110
+ 官方 DSH settings provider 会将这一 namespace 保存在 `$DSH_HOME/settings.yaml` 中(未设置 `DSH_HOME` 时回退到 `~/.dsh/settings.yaml`),并保留同一文档中的其他 settings namespace。下面是 v2 canonical YAML 结构:
111
+
112
+ ```yaml
113
+ proxy-routing:
114
+ version: 2
115
+ profiles:
116
+ - id: default
117
+ protocol: http
118
+ host: 127.0.0.1
119
+ port: 7897
120
+ username: ""
121
+ password: ""
122
+ noProxy: [127.0.0.1, localhost, ::1, api.deepseek.com]
123
+ timeout: 60000
124
+ bindings:
125
+ agent:
126
+ kind: direct
127
+ # 使用 profile 时还需:profileId: default
128
+ providers: []
129
+ gateway: null
130
+ gatewayPurposes: []
131
+ gateway:
132
+ enabled: false
133
+ port: 17890
134
+ dedicatedPurposePorts: false
135
+ purposes: []
136
+ ```
137
+
138
+ 其中 `protocol`、`host`、`port` 必须指向环境中已经运行的代理服务。插件通过 `ctx.settings` 注册 namespace;YAML 解析、原子写入、锁和热更新由官方 settings-file provider 负责。插件不会读取、写入、监听或迁移 `proxy-agent.json`。
139
+
140
+ 端点未配置时的固定指引:
141
+
142
+ > 代理端点未配置:Full Access 会话请运行 `net_proxy_discover` 查找已运行代理;受限会话请向用户询问 `protocol`/`host`/`port` 后运行 `net_proxy_probe`,再请求 `net_proxy_enable`。
143
+
144
+ ## Agent 安装指南
145
+
146
+ Agent 只负责发现并请求使用已经存在的代理,不负责启动代理服务。推荐流程:
147
+
148
+ 1. Full Access 会话先调用 `net_proxy_discover`;它只检查代理环境变量和有限的本机候选端口。受限会话不要主动扫描,先向用户询问 `protocol`、`host`、`port`。
149
+ 2. 外部请求、抓取或下载失败时,调用 `net_proxy_status`,确认 `default` 是否已配置。
150
+ 3. 调用 `net_proxy_probe` 测试当前或临时端点;未配置端点时不要猜测地址。
151
+ 4. 使用端点信息请求 `net_proxy_enable`;Full Access 等级下直接生效,其他等级等待人类批准后再重试失败操作。
152
+ 5. 需要恢复直连时,请求 `net_proxy_disable`。显式 provider 覆盖不会被清除。
153
+
154
+ ### 权限分级
155
+
156
+ | 权限等级 | `net_proxy_enable` / `net_proxy_disable` 行为 |
157
+ |---|---|
158
+ | Full Access(`danger-full-access`) | 无需询问用户,直接生效 |
159
+ | 其他等级(`read-only` / `workspace-write` 等) | 需要人类批准后生效 |
160
+
161
+ 只读工具 `net_proxy_status` / `net_proxy_probe` / `net_proxy_discover` 在任何等级下都无需审批。
162
+
163
+ 可用工具:
164
+
165
+ | 工具 | 审批 | 用途 |
166
+ |---|---|---|
167
+ | `net_proxy_status` | 只读 | 查看配置修订号、端点是否已配置、Agent 生效路由、provider 覆盖、fetch 归属和持久 shell 代际;可用 `verify` 探测连通性。 |
168
+ | `net_proxy_probe` | 只读 | 使用当前或临时端点测试链路,不修改配置。 |
169
+ | `net_proxy_discover` | 仅 Full Access | 检查代理环境变量和有限的本机候选端口,只返回探测成功候选,不启用或持久化路由。 |
170
+ | `net_proxy_enable` | 需要审批(Full Access 除外) | 将 Agent 路由指向 `default`;可更新端点字段并持久化。 |
171
+ | `net_proxy_disable` | 需要审批(Full Access 除外) | 将 Agent 路由恢复直连;provider 覆盖和端点配置保留。 |
172
+
173
+ ## 开发与测试
174
+
175
+ ```bash
176
+ pnpm install
177
+ pnpm test
178
+ pnpm run build:client
179
+ pnpm run probe
180
+ ```
181
+
182
+ `pnpm run build:client` 会生成 Web module-loader 使用的 `lib/client.js`。开发 client HMR 时,从同一 DSH checkout 运行 `pnpm run dev:web`;没有 watcher 时,重建 client artifact 后刷新注入 DSH Web 页面。
183
+
184
+ 测试覆盖 HTTP/CONNECT/SOCKS5、超时、背压、`NO_PROXY`、重定向、探测与有限发现、审批门禁、settings schema 校验与 YAML 热更新、loopback RPC 校验、补偿事务、fetch/LLM/shell 隔离、HMR/dispose 幂等和真实子进程继承。
185
+
186
+ ## 参考
187
+
188
+ - 传输层参考:[mafeis/dsh-net-proxy](https://github.com/mafeis/dsh-net-proxy)(MIT)。
189
+ - 配置命名参考:[@cordisjs/plugin-proxy-agent](https://www.npmjs.com/package/@cordisjs/plugin-proxy-agent)。
190
+
191
+ ## 许可证
192
+
193
+ MIT
@@ -0,0 +1,6 @@
1
+ # dsh-proxy-routing — network-proxy plugin bundle patch.
2
+ # 安装方式:把本文件内容并入 profile 的 cordis.patch.yml,
3
+ # 或 `dsh plugin --profile web add <path-to-this-package>`。
4
+ - insert:
5
+ - id: proxy-routing
6
+ name: 'dsh-proxy-routing'
package/lib/actions.js ADDED
@@ -0,0 +1,61 @@
1
+ // lib/actions.js — canonical 变更原语(工具/Web/测试共用;纯函数,校验交由调用方)
2
+ import { DEFAULT_PROFILE_ID } from "./config.js";
3
+
4
+ export function agentDirect(canonical) {
5
+ const next = structuredClone(canonical);
6
+ next.bindings.agent = { kind: "direct" };
7
+ return next;
8
+ }
9
+
10
+ export function agentProfile(canonical, profileId = DEFAULT_PROFILE_ID) {
11
+ const next = structuredClone(canonical);
12
+ next.bindings.agent = { kind: "profile", profileId };
13
+ return next;
14
+ }
15
+
16
+ export function setProfileEndpoint(canonical, { protocol, host, port, username, password, noProxy, timeout } = {}) {
17
+ const next = structuredClone(canonical);
18
+ const profile = next.profiles.find((p) => p.id === DEFAULT_PROFILE_ID);
19
+ if (!profile) throw new Error("missing default profile");
20
+ if (protocol === "http" || protocol === "socks5") profile.protocol = protocol;
21
+ if (typeof host === "string") profile.host = host;
22
+ if (port !== undefined) profile.port = port;
23
+ if (username !== undefined) profile.username = username;
24
+ if (password !== undefined) profile.password = password;
25
+ if (Array.isArray(noProxy)) profile.noProxy = noProxy;
26
+ if (timeout !== undefined) profile.timeout = timeout;
27
+ return next;
28
+ }
29
+
30
+ /** 启用:先(可选)更新端点,再把 Agent 路由指向 default profile。 */
31
+ export function enableProfile(canonical, endpoint) {
32
+ let next = canonical;
33
+ const hasEndpointArgs =
34
+ endpoint &&
35
+ (endpoint.protocol !== undefined || endpoint.host !== undefined ||
36
+ endpoint.port !== undefined || endpoint.username !== undefined ||
37
+ endpoint.password !== undefined || endpoint.noProxy !== undefined);
38
+ if (hasEndpointArgs) {
39
+ // 对齐 v1 语义:显式给端点(host/port)而缺 protocol 时按 http 处理;
40
+ // 绝不静默假设代理地址(host/port 本身仍由用户提供)。
41
+ const withProtocol = { ...endpoint };
42
+ if (endpoint.host !== undefined && endpoint.protocol === undefined) withProtocol.protocol = "http";
43
+ next = setProfileEndpoint(next, withProtocol);
44
+ }
45
+ return agentProfile(next, DEFAULT_PROFILE_ID);
46
+ }
47
+
48
+ export function setProviderRoute(canonical, providerId, route) {
49
+ const next = structuredClone(canonical);
50
+ next.bindings.providers = [
51
+ ...next.bindings.providers.filter((e) => e.provider !== providerId),
52
+ { provider: providerId, route },
53
+ ];
54
+ return next;
55
+ }
56
+
57
+ export function clearProviderRoute(canonical, providerId) {
58
+ const next = structuredClone(canonical);
59
+ next.bindings.providers = next.bindings.providers.filter((e) => e.provider !== providerId);
60
+ return next;
61
+ }
package/lib/als.js ADDED
@@ -0,0 +1,8 @@
1
+ // lib/als.js — 路由上下文:provider 与 shell 两条独立的 AsyncLocalStorage 通道
2
+ import { AsyncLocalStorage } from "node:async_hooks";
3
+
4
+ /** provider 上下文:不可变路由快照 + provider id + 修订号 + 请求代际。 */
5
+ export const providerStore = new AsyncLocalStorage();
6
+
7
+ /** shell 执行上下文:Agent 路由快照 + 修订号 + 策略代际 + agent 身份。 */
8
+ export const shellStore = new AsyncLocalStorage();