dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
DSH plugin: switch the local HTTP proxy port at runtime with /proxy <port>; routes the agent's in-process fetches and child processes
English | 中文
Route DeepSeek Harness's own network calls — web_fetch, fetch(), anything that goes through undici — through a local HTTP proxy, chosen from the composer instead of a config file.
you: /proxy 7897
plugin: proxy http://127.0.0.1:7897
web_fetch and fetch() both use it
bypass: loopback only
source: saved state (enabled, host, port, proxyUrl)web_fetch does not decide about proxies itself. It asks @deepseek-ai/dsh-http-proxy's proxyRouteFor(url), which reads that module's process-wide state: an installed policy, or "direct + DNS pinning". Two consequences follow.
web_fetch would still see "direct" and keep pinning DNS, while plain fetch() took another route. The two halves would disagree, and only one of them is the tool the model actually calls.HTTP_PROXY in the environment is not enough either. The module snapshots the launch environment, so a variable set after boot is not what it routes by.So this plugin drives the real thing, in the copy of the module the host already imported:
createRequire() against resources/app.asar resolves the true entry of @deepseek-ai/dsh-http-proxy; a profile plugin cannot resolve @deepseek-ai/* by name — see Install.import(pathToFileURL(entry)) loads that same file. ESM caches by URL, so this is the host's module instance, not a second copy.installProxyFromEnvironment(env, report) is called with a Map that answers env.get('http_proxy').value, the only shape that module reads.The installed policy replaces the global undici dispatcher with a ProxyAgent factory and publishes http_proxy / https_proxy / all_proxy / no_proxy into process.env, so libraries that read the environment — and spawned children — agree with it.
Loopback addresses always bypass the proxy. SOCKS and PAC are rejected by that module: only http: / https: proxies are accepted.
Requirements:
^0.2.1-rc.2 — the Electron build; the plugin locates the application archive to reach the host's own modules.CONNECT (Clash, Clash Verge, v2ray, mitmproxy, …).The package declares engines.dsh, so a host outside that range refuses it at install time instead of loading it into a silent direct route.
The plugin page's install dialog and the plugin_manager tool both accept a pnpm git spec. The repository root *is* the package, so this is the entire install:
github:Acr1mony/dsh-proxy-port#<commit>
git@github.com:Acr1mony/dsh-proxy-port.git#<commit>There is no npm release: the repository is the distribution channel.
Pin a commit rather than a branch: this plugin drives a process-wide policy, so "whatever main was that day" is a bad thing to have written down.
Before pnpm starts, the manager probes github.com with git ls-remote (5 s, credential helpers disabled). When the probe fails, the dialog reports failedAt: 'spec-host' and suggests a registry mirror, which cannot help a git dependency — use the SSH form instead, or a local checkout below.
Bulk fetches also need a working route to github.com. If a plain git clone of this repository dies partway through (invalid index-pack output, Connection was reset), point git at your local proxy first:
git config --global http.https://github.com.proxy http://127.0.0.1:7890file:/path/to/dsh-proxy-portThe checkout is linked rather than copied, so edits take effect on the next launch.
A profile plugin is a bundle: the plugin page lists bundles, so a bare loader row never appears there. From a checkout at /path/to/dsh-proxy-port, edit ~/.dsh/profiles//package.json (on the desktop build the profile is desktop):
{
"dependencies": { "dsh-proxy-port": "file:/path/to/dsh-proxy-port" },
"dsh": { "profile": { "bundles": ["…", "dsh-proxy-port"] } }
}Add a row override in the same directory's cordis.patch.yml — the row itself comes from this package's cordis.patch.yml, the profile only overrides defaults:
- id: dsh-proxy-port
name: dsh-proxy-port
config:
enabled: true
host: 127.0.0.1
port: 7890
proxyUrl: ''
noProxy: ''
childNodes: true
verifyUrl: https://example.com/Then install the dependency with the pnpm that ships inside the app (resources/runtime/primary-runtime/dependencies/pnpm/bin/pnpm.cjs install) and restart.
dsh plugin --profile desktop add … is not an alternative here: the desktop profile is managed exclusively by the Electron application and the CLI refuses it.
Restart the application — without HMR the newly installed bundle is only loaded on the next start — then pick the port:
/proxy 7897The port is a command, not a field. /proxy runs in the host, produces no model message, and costs no tokens.
Input · Effect /proxy 7897 · Use http://127.0.0.1:7897 now, and from the next start /proxy · /proxy status · Current address, whether web_fetch really uses it, bypass list, source /proxy off · /proxy on · Uninstall / reinstall the policy (off restores the route that was in force before it) /proxy http://10.0.0.5:3128 · Full URL — also for an https: proxy or a non-loopback host /proxy 10.0.0.5:3128 · host:port anything else · Usage text; nothing changes
/proxy needs DSH's commands service (part of the base layer and of the Web client). The plugin asks for it optionally: a headless host without a command surface keeps the tool and skips the command instead of staying unmounted.
/local-proxy.json, written by the command:
{ "enabled": true, "host": "127.0.0.1", "port": 7897, "proxyUrl": "" }Precedence: state file → row config → built-in defaults. The row config is what ships; the state file is what you chose. Nothing here *has* to be edited by hand.
local_proxyThe model-side view of the same state — for evidence, not convenience.
Action · What it answers status · Is the policy installed? Which module file, which proxy URL, does proxyRouteFor(verifyUrl) say proxied, what is persisted set · Same effect as /proxy: change host/port/enabled/noProxy, persist, apply immediately diagnose · TCP reachability of the proxy port plus the route for one URL — no business request test · A real request (global fetch, then optionally ctx.web.fetch) and which route it took
Row config keys, all optional:
Key · Default · Meaning enabled · true · false uninstalls the policy; every request goes direct host / port · 127.0.0.1 / 7890 · Proxy address; a bad port falls back to 7890 with a warning instead of failing the plugin proxyUrl · '' · Full proxy URL such as http://127.0.0.1:7897; wins over host/port noProxy · '' · Comma-separated bypass hosts; loopback is always bypassed childNodes · true · Publish the proxy variables plus NODE_USE_ENV_PROXY=1 for child processes verifyUrl · https://example.com/ · Target that status / diagnose / test use by default
node test/smoke.mjs # real traffic traverses a proxy; dispose restores the previous route
node test/apply.mjs # mount, tool + command registration, persisted toggle, unmount
node test/command.mjs # /proxy parsing, persistence, remount keeps the chosen portsmoke.mjs starts a local proxy and asserts that the environment is published, proxyRouteFor says proxied, fetch() really traverses the proxy, loopback stays direct, and dispose restores both the route and the environment.apply.mjs mounts with a fake host context: apply(ctx, config) must register the tool and /proxy, ctx.effect() must install the policy, a persisted change must toggle it, and the disposer must uninstall it.command.mjs drives the command itself, including rejected input (abc, 70000, 0, host:99999), on / off / status, a full URL, and a remount that must still run on the persisted port.The tests need a Node binary — no DSH installation — but one thing cannot come from this repository: an archive root to resolve the host's modules through. Plain Node cannot read a packed .asar, yet resolution is by path, so a *directory* with that name works. Copy @deepseek-ai/dsh-http-proxy and undici out of an installed DeepSeek Harness into /resources/app.asar/dsh/node_modules/, then point DSH_LOCAL_PROXY_FIXTURE at ` (the lookup also tries fixture/ in the repository and the author's private _probe tree). Without a HTTP proxy library present, apply.mjs` still passes — the plugin warns and skips the install, which is the fallback it ships.
fetch, undici, and libraries that read http_proxy. A shell's curl or Python's requests follow their own environment, which childNodes only publishes — whether a child inherits it is the host's spawn policy.resources/app.asar; DSH_APP_ASAR overrides the location for an unusual install.MIT