@karoc/dsh-proxy 0.1.2 → 0.1.4

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 CHANGED
@@ -1,5 +1,95 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.4] - 2026-09-25
4
+
5
+ ### Fixed
6
+
7
+ - **DSH 0.1.7 icon rename (upstream commit `4937343a5e`, released from
8
+ `0.1.7-alpha.1`).** `IconChevronDownOutline14`, `IconGlobeOutline14` and
9
+ `IconLinkOutline14` no longer exist in `dsh-client-ui-primitives` — the size
10
+ suffix moved into each artwork's default and the name now carries the stroke
11
+ weight — so all three resolved to `undefined` and the Settings → Smoothly
12
+ Proxy section crashed on render (`React error #130`,
13
+ `slot entry crashed in 'settings.section'`). `src/client/ProxySection.tsx`
14
+ now imports the `*Regular` variants the built-in pages use. Rendered sizes: the chevron and the globe
15
+ stay 14 px (their artwork defaults); only the link moves from 14 px to its
16
+ 16 px artwork default. **Support floor:** the client half requires dsh ≥ 0.1.7
17
+ **since v0.1.4** — **v0.1.3 remains the release for 0.1.2–0.1.6**.
18
+
19
+ - **Loopback was only recognised as the literal `127.0.0.1`.** The routing guard
20
+ was `/^(127\.0\.0\.1|localhost|::1|0\.0\.0\.0)$/`, so every other address in the
21
+ `127.0.0.0/8` block (e.g. `127.0.0.2`, used by interface aliases and some
22
+ container setups) was sent to the **upstream proxy** when it was listed in
23
+ `proxiedHosts` — contradicting the documented "loopback targets are always
24
+ direct" guarantee. The guard now matches the whole `127/8` block. The spec
25
+ assertion that was supposed to pin this was **vacuous** (it asserted the
26
+ loopback hosts against a `proxiedHosts` list that did not contain them, so it
27
+ returned "direct" for the unlisted-host reason and would have stayed green with
28
+ the guard deleted) — it now lists them, adds a positive control, and covers
29
+ `127.0.0.2`; verified red against the old guard before the fix.
30
+ - Dropped the dead `@deepseek-ai/dsh-client-runtime/client` entry from the
31
+ bundler externals and the client-boot spec: that package no longer ships with
32
+ DSH and nothing imports it (same stale reference as the `dsh.client.inject`
33
+ row removed above).
34
+
35
+ ### Tests
36
+
37
+ - **`npm run verify:all` — every gate, one command, bound to the commit.**
38
+ It runs script syntax, the spec suite, the guarantee gate, the negative
39
+ controls and the release gate, and writes `{ts, commit, node, steps[]}` to the
40
+ gitignored `lib/verify-report.json`. `npm run verify:fresh` FAILS when HEAD no
41
+ longer matches the recorded commit — because a green claim is only as good as
42
+ the commit it was measured on, and this repo deliberately re-points tags for
43
+ unpublished versions. A failed step is a failed verification, never an
44
+ "unknown".
45
+ - **Negative controls, now part of `npm test`** (`npm run test:controls`).
46
+ A gate nobody has seen fail is not evidence, so `scripts/test-negative-controls.mjs`
47
+ clones the committed tree per scenario, injects ONE defect, and asserts the
48
+ gate fails with the documented message: a dirty tree, a removed CHANGELOG
49
+ entry, a deleted release tag, a removed build artifact, and a guarantee row
50
+ whose pinning assertion no longer exists — plus a positive control (an
51
+ unmutated clone must pass both gates). 5/5 mutations caught.
52
+ - **Guarantee gate, now part of `npm test`.** `docs/guarantees.md` lists this
53
+ plugin's negative guarantees and names the assertion label that pins each one;
54
+ `scripts/check-guarantees.mjs` fails the suite when a label disappears (12 rows
55
+ today, and the selector must exist in `scripts/*.spec.mjs`). Verified by
56
+ negative control: pointing a row at a non-existent label makes the gate FAIL.
57
+ The routing guard that shipped matching only the literal `127.0.0.1` stayed
58
+ invisible for months precisely because the assertion meant to pin it could not
59
+ fail — the gate plus the `127/8` assertion close that class of defect.
60
+
61
+ ### Changed
62
+
63
+ - **The dsh floor is now declared, not just documented.** `package.json`
64
+ declares an optional peer dependency
65
+ `@deepseek-ai/dsh-client-ui-settings: ">=0.1.7-rc.1"`. The gate that reads it ships from **DSH 0.1.7-rc.1** on — it
66
+ compares every `@deepseek-ai/dsh*` peer against the running runtime and refuses
67
+ a plugin the runtime fails, printing the `dsh plugin allow-version` remedy.
68
+ Runtimes older than that gate evaluate **no** peers
69
+ and refuse nothing: on 0.1.2–0.1.6 the client half cannot render (the
70
+ `*Regular` icon names arrived in 0.1.7-alpha.1), so **v0.1.3 remains the
71
+ release for 0.1.2–0.1.6** — the 0.1.7 alphas have those icons and work. It is
72
+ marked `peerDependenciesMeta.optional` because the host supplies that package
73
+ at runtime, so npm installs nothing extra. Note the prerelease rule the range encodes (measured with the semver DSH
74
+ actually resolves — 7.8.5): `>=0.1.7` and `^0.1.7` do **not** match a
75
+ `0.1.7-rc.N` runtime, hence the explicit `-rc.1` floor. semver 7.7.4 answers
76
+ `true` for the caret form.
77
+ - Removed the stale `@deepseek-ai/dsh-client-runtime` entry from
78
+ `dsh.client.inject`: that package no longer ships with DSH (11 versions exist on npm, but `latest`
79
+ is still the historical `0.0.1-rc.1` and nothing in the harness tree provides
80
+ it), and the loader silently ignored the unresolvable row.
81
+
82
+ ## [0.1.3]
83
+
84
+ - **Brand name**: Smoothly Proxy / 思磨力代理插件 — the plugin's user-visible
85
+ identity is now standardized: 品牌英文 **Smoothly**,品牌中文 **思磨力**,英文名
86
+ **Smoothly Proxy**(简称同),中文名 **思磨力代理插件**(简称 **思磨力代理**)。
87
+ Applied to the READMEs, settings page title, settings nav label
88
+ (思磨力代理 / Smoothly Proxy), package description, and source header
89
+ comments. The technical identity is untouched: npm package
90
+ `@karoc/dsh-proxy`, runtime id `dsh-proxy`, CSS prefix, and `/proxy/api`
91
+ route all stay as-is so installed profiles need no re-install.
92
+
3
93
  ## [0.1.2]
4
94
 
5
95
  - **Brand name**: DSH Smoothly Proxy (DSH SP) — used in the README, settings page title, and changelog.
package/CONTRIBUTING.md CHANGED
@@ -8,7 +8,8 @@ model-provider proxying.
8
8
  ```sh
9
9
  pnpm install # dev deps (tsdown, typescript, react types)
10
10
  pnpm typecheck # tsc --noEmit
11
- pnpm test # typecheck + scripts/proxy-core.spec.mjs (13 scenarios)
11
+ pnpm test # node --test: proxy-core (13 scenarios) + host-route /
12
+ # client-boot / cordis-mount specs (no external network)
12
13
  pnpm bundle # emit lib/index.js + lib/client.js
13
14
  ```
14
15
 
@@ -24,7 +25,7 @@ To see the plugin live in the GUI, link it into a profile:
24
25
  ```jsonc
25
26
  // ~/.dsh/profiles/web/package.json
26
27
  "dependencies": { "@karoc/dsh-proxy": "link:/path/to/dsh-proxy", ... },
27
- "dsh": { "profile": { "bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", ..., "dsh-proxy"] } }
28
+ "dsh": { "profile": { "bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", ..., "@karoc/dsh-proxy"] } }
28
29
  ```
29
30
 
30
31
  Then `cd ~/.dsh/profiles/web && pnpm install` and **restart `dsh web`**
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # DSH Smoothly Proxy (DSH SP)
1
+ # Smoothly Proxy (思磨力代理插件)
2
2
 
3
3
  English | [简体中文](README.zh.md)
4
4
 
@@ -6,9 +6,9 @@ English | [简体中文](README.zh.md)
6
6
  [![npm downloads](https://img.shields.io/npm/dm/@karoc/dsh-proxy.svg)](https://www.npmjs.com/package/@karoc/dsh-proxy)
7
7
  [![license MIT](https://img.shields.io/npm/l/@karoc/dsh-proxy.svg)](LICENSE)
8
8
 
9
- DSH Smoothly Proxy (**DSH SP**) is an **external** DeepSeek Harness plugin: a
10
- model-provider **forward proxy** with **per-host routing**, plus a **Settings
11
- page** dedicated to model-provider proxying.
9
+ **Smoothly Proxy** (**思磨力代理** / Smoothly) is an **external** DeepSeek
10
+ Harness plugin: a model-provider **forward proxy** with **per-host routing**,
11
+ plus a **Settings page** dedicated to model-provider proxying.
12
12
 
13
13
  It runs a loopback forward proxy inside the dsh host process, points the
14
14
  process's egress traffic at it (`HTTP(S)_PROXY` + `NODE_USE_ENV_PROXY`), and
@@ -29,12 +29,15 @@ desktop shell had.
29
29
 
30
30
  ## What it adds
31
31
 
32
- A new Settings section, **「代理 / Proxy」**, placed after the built-in
33
- **Models** and **Model reasoning** pages. It shows:
32
+ A new Settings section, **「思磨力代理 / Smoothly Proxy」** (nav order 25), placed
33
+ after the built-in **Models** page and, when that plugin is installed, the
34
+ external **Model reasoning** page. It shows:
34
35
 
35
36
  - an **upstream proxy card**: enable toggle, protocol selector (HTTP / HTTPS /
36
37
  SOCKS5), host, port, optional username/password, and a **Test connection**
37
- button that verifies the upstream speaks its protocol;
38
+ button (a SOCKS5 handshake for SOCKS5, a `CONNECT` probe for HTTP; an HTTPS
39
+ upstream is only checked for TCP reachability — its TLS handshake is not
40
+ verified);
38
41
  - a **model providers** list — hosts read from your dsh `settings.yaml`
39
42
  (`llm-deepseek.baseURL`, `llm-pi-ai.providers.<n>.baseURL`, any `llm-*`
40
43
  namespace), labeled with the friendly display name where available;
@@ -104,7 +107,7 @@ The package is published to npm as `@karoc/dsh-proxy`:
104
107
  dsh plugin --profile web add @karoc/dsh-proxy
105
108
  ```
106
109
 
107
- This installs the prebuilt bundle and appends it to the `web` profile. Then **restart `dsh web`** and open **Settings → 代理 / Proxy**.
110
+ This installs the prebuilt bundle and appends it to the `web` profile. Then **restart `dsh web`** and open **Settings → 思磨力代理 / Smoothly Proxy**.
108
111
 
109
112
  ### From git
110
113
 
@@ -119,8 +122,8 @@ A git install runs the package's `prepare` script to build the bundle. pnpm ≥
119
122
  Bump to the newest release with pnpm update (or re-add to pick up a newer git ref):
120
123
 
121
124
  ```sh
122
- dsh plugin --profile web update dsh-proxy
123
- # or, if the dependency spec is pinned: dsh plugin --profile web add dsh-proxy
125
+ dsh plugin --profile web update @karoc/dsh-proxy
126
+ # or, if the dependency spec is pinned: dsh plugin --profile web add @karoc/dsh-proxy
124
127
  ```
125
128
 
126
129
  Then **restart `dsh web`** so the new client bundle loads.
@@ -128,7 +131,7 @@ Then **restart `dsh web`** so the new client bundle loads.
128
131
  ### Removing
129
132
 
130
133
  ```sh
131
- dsh plugin --profile web remove dsh-proxy
134
+ dsh plugin --profile web remove @karoc/dsh-proxy
132
135
  ```
133
136
 
134
137
  This removes both the dependency and its bundle layer from the `web` profile. Restart `dsh web` for the section to disappear.
@@ -149,7 +152,8 @@ src/client/index.ts # client apply: register settings.section (id dsh-proxy)
149
152
  src/client/ProxySection.tsx # the settings page (upstream card + host lists)
150
153
  src/client/styles.ts # design-token styles (--dsw-alias-*) + injection
151
154
  src/client/locales.ts # en/zh copy
152
- scripts/proxy-core.spec.mjs # behavioral tests (13 scenarios, no network)
155
+ scripts/*.spec.mjs # behavioral tests: proxy-core.ts (13 scenarios) plus
156
+ # host route / client boot / cordis mount — no network
153
157
  ```
154
158
 
155
159
  ### The /proxy/api route
@@ -157,7 +161,7 @@ scripts/proxy-core.spec.mjs # behavioral tests (13 scenarios, no network)
157
161
  The host half serves the settings page over a same-origin HTTP route (the
158
162
  built-in `/api` prefix is reserved for the gateway, so this uses `/proxy/api`):
159
163
 
160
- - `GET /proxy/api` → `{ upstream, proxiedHosts, knownHosts, hosts, providers, port }`
164
+ - `GET /proxy/api` → `{ ok, upstream, proxiedHosts, knownHosts, hosts, providers, port }`
161
165
  - `POST /proxy/api` `{ op: 'save', upstream, proxiedHosts }` → sanitized + persisted config
162
166
  - `POST /proxy/api` `{ op: 'test', upstream }` → `{ ok, detail }`
163
167
  - `POST /proxy/api` `{ op: 'persist' }` → merge observed hosts into `knownHosts`
@@ -167,7 +171,9 @@ built-in `/api` prefix is reserved for the gateway, so this uses `/proxy/api`):
167
171
  ```sh
168
172
  pnpm install
169
173
  pnpm bundle # emits lib/index.js + lib/client.js
170
- pnpm test # tsc --noEmit + proxy-core.spec.mjs (13 scenarios)
174
+ pnpm typecheck # tsc --noEmit
175
+ pnpm test # node --test: proxy-core (13 scenarios) + host-route /
176
+ # client-boot / cordis-mount specs (no network)
171
177
  pnpm release:check # release gate: docs/changelog/tag/tree/build/registry must all pass
172
178
  pnpm publish # runs the gate (prepack/prepublishOnly), then postpublish verifies the live release
173
179
  ```
@@ -189,18 +195,31 @@ module table; everything else is inlined.
189
195
  - `npm`/`pnpm` install/update traffic goes through the proxy like everything
190
196
  else; toggling a host **related to install/update** takes effect on the next
191
197
  install/update (already-in-flight operations keep their environment).
198
+ - **A SOCKS5 upstream only carries HTTPS targets.** SOCKS5 has no absolute-URI
199
+ HTTP mode, so a selected plain `http://` host falls back to direct through a
200
+ SOCKS5 upstream (the settings page states the same thing).
192
201
  - **Section nav icon is shell-assigned, not plugin-assigned.** The built-in
193
202
  `ui-settings-general` `SettingsRoot.tsx` `navIcon(id)` maps known ids and
194
203
  falls back to a gear for every other id — including this section's
195
204
  `dsh-proxy`. The `settings.section` registration has no icon field, so an
196
205
  external plugin cannot set it without patching the shell. When DSH exposes a
197
- per-section icon, use `IconGlobeOutline14` from `dsh-client-ui-primitives`
206
+ per-section icon, use `IconGlobeOutlineRegular` from `dsh-client-ui-primitives`
198
207
  for this section.
199
- - **Works with dsh ≥ 0.1.2 when installed the official way.** The loader
208
+ - **Requires dsh ≥ 0.1.7 since v0.1.4 — v0.1.3 remains the release for
209
+ 0.1.2–0.1.6.** DSH 0.1.7 renamed the icon exports (`IconX14` → `IconXRegular`,
210
+ the stroke-weight naming; upstream commit `4937343a5e`), so this section
211
+ imports the `*Regular` variants. The floor is declared as an **optional peer
212
+ dependency** on `@deepseek-ai/dsh-client-ui-settings`, which the dsh ≥ 0.1.7
213
+ compatibility gate compares against the running dsh version — a mismatch
214
+ refuses the bundle and names the exact `dsh plugin allow-version` remedy.
215
+ dsh < 0.1.7 predates that gate, so those runtimes still load this plugin and
216
+ render a broken section — install v0.1.3 there.
217
+ - **Install it the official way.** The loader
200
218
  entry name, bundle registration id, and host plugin name are all
201
- `@karoc/dsh-proxy` (matching the npm package name). Hand-written `link:`
202
- dependencies are not recognized as packages by newer loaders — install via
203
- `dsh plugin --profile web add @karoc/dsh-proxy` (npm) or
219
+ `@karoc/dsh-proxy` (matching the npm package name). A hand-written `link:`
220
+ dependency only counts once the package manager has materialized it under the
221
+ profile's `node_modules` and the profile lists it in `dsh.profile.bundles` —
222
+ install via `dsh plugin --profile web add @karoc/dsh-proxy` (npm) or
204
223
  `dsh plugin --profile web add link:/path/to/dsh-proxy` (source), then restart
205
224
  `dsh web`.
206
225
  - The desktop shell (`dsh-desktop`) keeps its own proxy and tray settings
package/README.zh.md CHANGED
@@ -1,4 +1,4 @@
1
- # DSH Smoothly Proxy(DSH SP)
1
+ # 思磨力代理插件(Smoothly Proxy)
2
2
 
3
3
  [English](README.md) | 简体中文
4
4
 
@@ -6,7 +6,7 @@
6
6
  [![npm downloads](https://img.shields.io/npm/dm/@karoc/dsh-proxy.svg)](https://www.npmjs.com/package/@karoc/dsh-proxy)
7
7
  [![license MIT](https://img.shields.io/npm/l/@karoc/dsh-proxy.svg)](LICENSE)
8
8
 
9
- DSH Smoothly Proxy(**DSH SP**)是一个**外部** DeepSeek Harness 插件:为**模型提供方**提供带**按主机路由**的**正向代理**,并附带一个专职设置页。
9
+ **思磨力代理插件**(**Smoothly Proxy** / 思磨力)是一个**外部** DeepSeek Harness 插件:为**模型提供方**提供带**按主机路由**的**正向代理**,并附带一个专职设置页。
10
10
 
11
11
  它在 dsh host 进程内启动一个 loopback 正向代理,把进程的出站流量指向它(`HTTP(S)_PROXY` + `NODE_USE_ENV_PROXY`),**只把你在设置页勾选的主机**转发到可选的上游代理(HTTP / HTTPS / SOCKS5,支持可选 Basic 认证),其余全部直连。模型提供方主机从你的 dsh `settings.yaml` 读取,流量中观测到的主机也会被收集——两者都以复选框形式出现。
12
12
 
@@ -16,9 +16,9 @@ DSH Smoothly Proxy(**DSH SP**)是一个**外部** DeepSeek Harness 插件:
16
16
 
17
17
  ## 它新增了什么
18
18
 
19
- 一个放在内置 **Models** 与 **Model reasoning** 页之后的新设置项 **「代理 / Proxy」**,包含:
19
+ 一个新的设置项 **「思磨力代理 / Smoothly Proxy」**(导航顺序 25),排在内置 **Models** 页以及(若装了该插件)外部 **Model reasoning** 页之后,包含:
20
20
 
21
- - **上游代理卡片**:启用开关、协议选择(HTTP / HTTPS / SOCKS5)、主机、端口、可选用户名/密码,以及**测试连接**按钮(验证上游是否会说对应协议);
21
+ - **上游代理卡片**:启用开关、协议选择(HTTP / HTTPS / SOCKS5)、主机、端口、可选用户名/密码,以及**测试连接**按钮(SOCKS5 上游走 SOCKS5 握手、HTTP 上游走 `CONNECT` 探测;HTTPS 上游只验证 TCP 可达——不校验其 TLS 握手);
22
22
  - **模型提供方**列表——从你的 dsh `settings.yaml` 读取的主机(`llm-deepseek.baseURL`、`llm-pi-ai.providers.<n>.baseURL`、任意 `llm-*` 命名空间),有友好显示名时以显示名标注;
23
23
  - **其它已观测主机**列表——代理在流量中见过的主机(含 `registry.npmjs.org` 等安装/更新流量),持久化进 `proxy.json` 以跨重启保留;
24
24
  - **搜索框**——输入时按主机 / 名称过滤上面两个列表。
@@ -78,7 +78,7 @@ dsh host 进程
78
78
  dsh plugin --profile web add @karoc/dsh-proxy
79
79
  ```
80
80
 
81
- 安装预构建 bundle 并追加到 `web` profile。然后**重启 `dsh web`**,打开 **设置 → 代理 / Proxy**。
81
+ 安装预构建 bundle 并追加到 `web` profile。然后**重启 `dsh web`**,打开 **设置 → 思磨力代理 / Smoothly Proxy**。
82
82
 
83
83
  ### 从 git 安装
84
84
 
@@ -93,8 +93,8 @@ git 安装会运行包的 `prepare` 脚本构建 bundle。pnpm ≥ 10 需要先
93
93
  用 pnpm update 升到最新版(或重 `add` 以拉取更新的 git ref):
94
94
 
95
95
  ```sh
96
- dsh plugin --profile web update dsh-proxy
97
- # 或,若依赖规格被 pin 住:dsh plugin --profile web add dsh-proxy
96
+ dsh plugin --profile web update @karoc/dsh-proxy
97
+ # 或,若依赖规格被 pin 住:dsh plugin --profile web add @karoc/dsh-proxy
98
98
  ```
99
99
 
100
100
  然后**重启 `dsh web`** 以加载新客户端 bundle。
@@ -102,7 +102,7 @@ dsh plugin --profile web update dsh-proxy
102
102
  ### 卸载
103
103
 
104
104
  ```sh
105
- dsh plugin --profile web remove dsh-proxy
105
+ dsh plugin --profile web remove @karoc/dsh-proxy
106
106
  ```
107
107
 
108
108
  同时从 `web` profile 移除依赖与 bundle 层。重启 `dsh web` 后该设置项消失。
@@ -120,14 +120,15 @@ src/client/index.ts # client apply:注册 settings.section(id dsh-proxy)
120
120
  src/client/ProxySection.tsx # 设置页(上游卡片 + 主机列表)
121
121
  src/client/styles.ts # design-token 样式(--dsw-alias-*)+ 注入
122
122
  src/client/locales.ts # en/zh 文案
123
- scripts/proxy-core.spec.mjs # 行为测试(13 场景,无外部网络)
123
+ scripts/*.spec.mjs # 行为测试:proxy-core.ts(13 场景)+ host route / client
124
+ # boot / cordis mount,全程无外部网络
124
125
  ```
125
126
 
126
127
  ### /proxy/api 路由
127
128
 
128
129
  host 半区用同源 HTTP 路由为设置页供数(内置 `/api` 前缀被 gateway 占用,故用 `/proxy/api`):
129
130
 
130
- - `GET /proxy/api` → `{ upstream, proxiedHosts, knownHosts, hosts, providers, port }`
131
+ - `GET /proxy/api` → `{ ok, upstream, proxiedHosts, knownHosts, hosts, providers, port }`
131
132
  - `POST /proxy/api` `{ op: 'save', upstream, proxiedHosts }` → 清洗后持久化的配置
132
133
  - `POST /proxy/api` `{ op: 'test', upstream }` → `{ ok, detail }`
133
134
  - `POST /proxy/api` `{ op: 'persist' }` → 把观测主机并入 `knownHosts`
@@ -137,7 +138,9 @@ host 半区用同源 HTTP 路由为设置页供数(内置 `/api` 前缀被 gat
137
138
  ```sh
138
139
  pnpm install
139
140
  pnpm bundle # 产出 lib/index.js + lib/client.js
140
- pnpm test # tsc --noEmit + proxy-core.spec.mjs(13 场景)
141
+ pnpm typecheck # tsc --noEmit
142
+ pnpm test # node --test:proxy-core(13 场景)+ host-route /
143
+ # client-boot / cordis-mount,无外部网络
141
144
  pnpm release:check # 发布门禁:文档/changelog/tag/工作区/构建/registry 全过
142
145
  pnpm publish # 跑门禁(prepack/prepublishOnly),随后 postpublish 验证线上发布
143
146
  ```
@@ -148,8 +151,10 @@ bundle 把平台包(`react`、`@deepseek-ai/cordis`、`@deepseek-ai/dsh-client
148
151
 
149
152
  - **代理是进程级出站点,不是模型逐请求读的开关。** 它通过把进程的 `HTTP(S)_PROXY` 指向自身来实现。本插件 host `apply()` 之后 dsh 发起的请求(任何子进程、以及 global dispatcher 仍是惰性的 undici fetch)会走代理。若某个 dsh 内部 fetch 在插件加载前就已实体化了 dispatcher,可能不会走它——文档化的解法是重启 dsh(桌面壳通过 spawn dsh 前设环境变量来施加同样的边界)。
150
153
  - `npm`/`pnpm` 安装/更新流量与其它一样走代理;改动**与安装/更新相关的主机**会在下次安装/更新时生效(已在进行的操作保留其环境)。
151
- - **设置页导航图标由壳分配,插件无法自定义。** 内置 `ui-settings-general` 的 `SettingsRoot.tsx` `navIcon(id)` 只映射已知 id,其它 id(包括本节的 `dsh-proxy`)一律齿轮。`settings.section` 注册没有 icon 字段,外部插件不 patch 壳就改不了。等 DSH 开放每节图标后,本节建议用 `dsh-client-ui-primitives` 的 `IconGlobeOutline14`。
152
- - **dsh ≥ 0.1.2 需以官方方式安装才生效。** loader entry 名、bundle 注册 id、host 插件名三者均为 `@karoc/dsh-proxy`(与 npm 包名一致)。新版 loader 不再把手写 `link:` 依赖识别为包——请用 `dsh plugin --profile web add @karoc/dsh-proxy`(npm)或 `dsh plugin --profile web add link:/path/to/dsh-proxy`(源码)安装,然后重启 `dsh web`。
154
+ - **SOCKS5 上游只承载 HTTPS 目标。** SOCKS5 没有 absolute-URI 的 HTTP 模式,因此选中的纯 `http://` 主机会退回直连(设置页也写明了这一点)。
155
+ - **设置页导航图标由壳分配,插件无法自定义。** 内置 `ui-settings-general` 的 `SettingsRoot.tsx` `navIcon(id)` 只映射已知 id,其它 id(包括本节的 `dsh-proxy`)一律齿轮。`settings.section` 注册没有 icon 字段,外部插件不 patch 壳就改不了。等 DSH 开放每节图标后,本节建议用 `dsh-client-ui-primitives` 的 `IconGlobeOutlineRegular`。
156
+ - **本节自 v0.1.4 起要求 dsh ≥ 0.1.7(0.1.2–0.1.6 请用 v0.1.3)。** DSH 0.1.7 改了图标导出名(`IconX14` → `IconXRegular`,尺寸后缀并入 artwork 默认值、名字改为承载笔画权重;上游 commit `4937343a5e`),本节现在导入 `*Regular` 变体。该下限已声明为对 `@deepseek-ai/dsh-client-ui-settings` 的**可选 peer 依赖**,由 dsh ≥ 0.1.7 的兼容门禁按运行时版本判定(不匹配则拒绝加载该 bundle,并给出 `dsh plugin allow-version` 的具体解法)。0.1.7 之前的 dsh 没有这道门禁,仍会加载本插件并渲染出坏掉的分区——那些运行时请用 v0.1.3。
157
+ - **需以官方方式安装才生效。** loader entry 名、bundle 注册 id、host 插件名三者均为 `@karoc/dsh-proxy`(与 npm 包名一致)。手写 `link:` 依赖只有在包管理器把它落到 profile 的 `node_modules` 下、且 profile 的 `dsh.profile.bundles` 列出它之后才算数——请用 `dsh plugin --profile web add @karoc/dsh-proxy`(npm)或 `dsh plugin --profile web add link:/path/to/dsh-proxy`(源码)安装,然后重启 `dsh web`。
153
158
  - 桌面壳(`dsh-desktop`)保留自己的代理与托盘设置窗口——本插件是独立抽取,不是替代品。两者可共存(例如把 `dsh-proxy` 装进壳的 profile,同时获得 dsh 内设置页)。
154
159
 
155
160
  ## License
package/cordis.patch.yml CHANGED
@@ -1,6 +1,7 @@
1
1
  # Bundle layer applied when a profile lists this package. The row names the
2
2
  # package so the host loader resolves the node half and client-modules serves
3
- # exports["./client"] at /plugins/dsh-proxy/client.js. The host half starts the
3
+ # exports["./client"] at /plugins/@karoc/dsh-proxy/client.js (the browser row
4
+ # id is the package name, not the runtime plugin id). The host half starts the
4
5
  # loopback forward proxy and registers /proxy/api; the client half registers
5
6
  # the settings.section settings page. The loader entry `name` must equal the
6
7
  # npm package name (@karoc/dsh-proxy) — client-modules resolves it as a
package/lib/client.js CHANGED
@@ -9,7 +9,7 @@ window.__ModuleLoader__.load({
9
9
  let react_jsx_runtime = require("react/jsx-runtime");
10
10
  //#region src/client/ProxySection.tsx
11
11
  /**
12
- * The "Proxy" settings section (external plugin).
12
+ * The "Smoothly Proxy" settings section (external plugin).
13
13
  *
14
14
  * A companion page to the built-in Models page: it configures a loopback
15
15
  * forward proxy that routes model-provider traffic (and any other selected
@@ -76,7 +76,7 @@ window.__ModuleLoader__.load({
76
76
  onClick: () => {
77
77
  setOpen((v) => !v);
78
78
  },
79
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: matched?.label ?? t("protocolHttp") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {})]
79
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: matched?.label ?? t("protocolHttp") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, {})]
80
80
  })
81
81
  });
82
82
  }
@@ -370,7 +370,7 @@ window.__ModuleLoader__.load({
370
370
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("proxiedHostsHint") }),
371
371
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
372
372
  className: "dsh-proxy-search",
373
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconGlobeOutline14, {}),
373
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconGlobeOutlineRegular, {}),
374
374
  placeholder: t("searchPlaceholder"),
375
375
  value: query,
376
376
  onChange: (e) => {
@@ -427,7 +427,7 @@ window.__ModuleLoader__.load({
427
427
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
428
428
  variant: "outline",
429
429
  size: "md",
430
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconLinkOutline14, {}),
430
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconLinkOutlineRegular, {}),
431
431
  disabled: testing || !enabled,
432
432
  onClick: () => {
433
433
  test();
@@ -528,13 +528,13 @@ window.__ModuleLoader__.load({
528
528
  //#endregion
529
529
  //#region src/client/locales.ts
530
530
  /**
531
- * Copy dictionaries for the Proxy settings section (DSH Smoothly Proxy / DSH SP).
532
- * English is the key source (en object keyed by the same strings), zh must
533
- * carry every key.
531
+ * Copy dictionaries for the Smoothly Proxy settings section
532
+ * (Smoothly Proxy / 思磨力代理插件). English is the key source (en object
533
+ * keyed by the same strings), zh must carry every key.
534
534
  */
535
535
  const en = {
536
- nav: "Proxy",
537
- title: "DSH Smoothly Proxy (DSH SP)",
536
+ nav: "Smoothly Proxy",
537
+ title: "Smoothly Proxy",
538
538
  subtitle: "Model-provider traffic through an optional upstream proxy, per host.",
539
539
  upstreamTitle: "Upstream proxy (optional)",
540
540
  upstreamEnabled: "Enable upstream proxy (when off, every host connects directly)",
@@ -571,8 +571,8 @@ window.__ModuleLoader__.load({
571
571
  teardown: "Proxy engine stopped"
572
572
  };
573
573
  const zh = {
574
- nav: "代理",
575
- title: "DSH Smoothly Proxy(DSH SP)",
574
+ nav: "思磨力代理",
575
+ title: "思磨力代理插件",
576
576
  subtitle: "按主机把模型提供方流量转发到可选的上游代理。",
577
577
  upstreamTitle: "上游代理(可选)",
578
578
  upstreamEnabled: "启用上游代理(未启用时所有主机直连)",
package/lib/index.js CHANGED
@@ -34,7 +34,7 @@ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
34
34
  * * the proxy itself uses raw net/http (never undici's env-proxy), so it can
35
35
  * never route its own outbound connections back through itself.
36
36
  */
37
- const LOOPBACK = /^(127\.0\.0\.1|localhost|::1|0\.0\.0\.0)$/i;
37
+ const LOOPBACK = /^(127\.\d{1,3}\.\d{1,3}\.\d{1,3}|localhost|::1|0\.0\.0\.0)$/i;
38
38
  /** Upstream proxy protocol, defaulting to http (legacy configs have no field). */
39
39
  function upstreamProtocol(cfg) {
40
40
  return String(cfg?.upstream?.protocol || "http").toLowerCase();
package/package.json CHANGED
@@ -1,12 +1,16 @@
1
1
  {
2
2
  "name": "@karoc/dsh-proxy",
3
- "version": "0.1.2",
4
- "description": "DSH Smoothly Proxy (DSH SP): external DeepSeek Harness plugin — a model-provider forward proxy with per-host routing and a Settings page.",
3
+ "version": "0.1.4",
4
+ "description": "Smoothly Proxy (思磨力代理插件): external DeepSeek Harness plugin — a model-provider forward proxy with per-host routing and a Settings page.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "exports": {
8
- ".": { "default": "./lib/index.js" },
9
- "./client": { "default": "./lib/client.js" },
8
+ ".": {
9
+ "default": "./lib/index.js"
10
+ },
11
+ "./client": {
12
+ "default": "./lib/client.js"
13
+ },
10
14
  "./package.json": "./package.json"
11
15
  },
12
16
  "files": [
@@ -20,11 +24,12 @@
20
24
  "LICENSE"
21
25
  ],
22
26
  "dsh": {
23
- "bundle": { "patch": "./cordis.patch.yml" },
27
+ "bundle": {
28
+ "patch": "./cordis.patch.yml"
29
+ },
24
30
  "client": {
25
31
  "inject": [
26
32
  "@deepseek-ai/dsh-client-locale",
27
- "@deepseek-ai/dsh-client-runtime",
28
33
  "@deepseek-ai/dsh-client-ui-settings",
29
34
  "@deepseek-ai/dsh-client-ui-slots",
30
35
  "@deepseek-ai/dsh-client-ui-primitives"
@@ -36,21 +41,43 @@
36
41
  "bundle": "tsdown --config ./tsdown.config.ts",
37
42
  "prepare": "tsdown --config ./tsdown.config.ts",
38
43
  "typecheck": "tsc --noEmit",
39
- "test": "node --test scripts/proxy-core.spec.mjs scripts/host-route.spec.mjs scripts/client-boot.spec.mjs scripts/cordis-mount.spec.mjs",
44
+ "test": "node --test scripts/proxy-core.spec.mjs scripts/host-route.spec.mjs scripts/client-boot.spec.mjs scripts/cordis-mount.spec.mjs && node scripts/check-guarantees.mjs && node scripts/test-negative-controls.mjs",
40
45
  "accept": "node scripts/accept-gui.mjs && node scripts/verify-tooltip.mjs",
41
46
  "release:check": "node scripts/release-check.mjs",
42
47
  "prepack": "node scripts/release-check.mjs",
43
48
  "prepublishOnly": "node scripts/release-check.mjs && tsdown --config ./tsdown.config.ts",
44
- "postpublish": "node scripts/post-publish-check.mjs"
49
+ "postpublish": "node scripts/post-publish-check.mjs",
50
+ "verify:guarantees": "node scripts/check-guarantees.mjs",
51
+ "test:controls": "node scripts/test-negative-controls.mjs",
52
+ "verify:all": "node scripts/verify-all.mjs",
53
+ "verify:fresh": "node scripts/verify-all.mjs --check-fresh"
45
54
  },
46
- "engines": { "node": ">=18" },
47
- "keywords": ["deepseek-harness", "dsh", "plugin", "proxy", "model-provider", "settings", "smoothly-proxy", "sp"],
55
+ "engines": {
56
+ "node": ">=18"
57
+ },
58
+ "keywords": [
59
+ "deepseek-harness",
60
+ "dsh",
61
+ "plugin",
62
+ "proxy",
63
+ "model-provider",
64
+ "settings",
65
+ "smoothly-proxy",
66
+ "sp"
67
+ ],
48
68
  "author": "karoc",
49
69
  "license": "MIT",
50
- "repository": { "type": "git", "url": "git+https://github.com/karoc/dsh-proxy.git" },
51
- "bugs": { "url": "https://github.com/karoc/dsh-proxy/issues" },
70
+ "repository": {
71
+ "type": "git",
72
+ "url": "git+https://github.com/karoc/dsh-proxy.git"
73
+ },
74
+ "bugs": {
75
+ "url": "https://github.com/karoc/dsh-proxy/issues"
76
+ },
52
77
  "homepage": "https://github.com/karoc/dsh-proxy",
53
- "publishConfig": { "access": "public" },
78
+ "publishConfig": {
79
+ "access": "public"
80
+ },
54
81
  "devDependencies": {
55
82
  "@types/node": "^22.0.0",
56
83
  "@types/react": "~18.3.1",
@@ -58,5 +85,13 @@
58
85
  "tsdown": "^0.22.0",
59
86
  "typescript": "^5.6.0"
60
87
  },
61
- "peerDependencies": { "react": "^18.2.0" }
88
+ "peerDependencies": {
89
+ "@deepseek-ai/dsh-client-ui-settings": ">=0.1.7-rc.1",
90
+ "react": "^18.2.0"
91
+ },
92
+ "peerDependenciesMeta": {
93
+ "@deepseek-ai/dsh-client-ui-settings": {
94
+ "optional": true
95
+ }
96
+ }
62
97
  }