feat(server,web,core): explicit proxy address setting (no env vars needed) (#233)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Yaowei Zheng
2026-08-07 18:53:55 +08:00
committed by GitHub
parent 6e1c7cc28e
commit 0cc0bd1a1a
25 changed files with 948 additions and 278 deletions
+6 -1
View File
@@ -127,9 +127,14 @@ interface EnvironmentConfig {
sessionScratchpadDir?: string; // this Session's scratchpad (scratchpad/<sessionId>); enables truncated-output recovery
services?: EnvironmentServices; // runtime services injected into individual tools
vault?: Record<string, string>; // Vault env vars, injected into exec_command / input_command subprocesses
stripProxyEnv?: () => boolean; // true = strip HTTP(S)_PROXY/ALL_PROXY (NO_PROXY kept) from command subprocesses; re-read per spawn, absent = proxy allowed
proxyEnv?: () => ProxyEnvPolicy | null; // command-subprocess proxy policy; re-read per spawn, absent or null = pass through
}
// "strip" removes HTTP(S)_PROXY/ALL_PROXY (NO_PROXY kept); "inject" forces the explicit
// proxy over the inherited env: HTTP(S)_PROXY (+ lowercase twins) = url, NO_PROXY = noProxy
// (supplied pre-merged by the caller), inherited ALL_PROXY removed. Vault entries still win.
type ProxyEnvPolicy = { mode: "strip" } | { mode: "inject"; url: string; noProxy: string };
interface EnvironmentServices {
subagentRunner?: SubagentRunner; // needed by run_subagent
visionDescriber?: VisionDescriberService; // needed by describe_image on text-only models
+6 -1
View File
@@ -127,9 +127,14 @@ interface EnvironmentConfig {
sessionScratchpadDir?: string; // 本 Session 的 scratchpad(scratchpad/<sessionId>),提供后启用截断输出恢复
services?: EnvironmentServices; // 注入给个别工具的运行时服务
vault?: Record<string, string>; // Vault 环境变量,注入 exec_command / input_command 子进程
stripProxyEnv?: () => boolean; // 返回 true 时从命令子进程剥除 HTTP(S)_PROXY/ALL_PROXY(保留 NO_PROXY);每次 spawn 重读,缺省即允许代理
proxyEnv?: () => ProxyEnvPolicy | null; // 命令子进程代理策略;每次 spawn 重读,缺省或 null 即原样透传
}
// "strip" 剥除 HTTP(S)_PROXY/ALL_PROXY(保留 NO_PROXY);"inject" 以显式代理覆盖继承环境:
// HTTP(S)_PROXY(含小写拼写)= url、NO_PROXY = noProxy(由调用方预先合并),继承的 ALL_PROXY
// 一并移除。Vault 条目仍然优先。
type ProxyEnvPolicy = { mode: "strip" } | { mode: "inject"; url: string; noProxy: string };
interface EnvironmentServices {
subagentRunner?: SubagentRunner; // run_subagent 所需
visionDescriber?: VisionDescriberService; // text-only 模型的 describe_image 所需
+8 -2
View File
@@ -73,10 +73,16 @@ In desktop mode (the server spawned by the desktop app) the whole surface answer
| Method | Path | Description |
| --- | --- | --- |
| GET | /api/admin/settings | Server-global settings: `{settings: {useSystemProxy}}` |
| GET | /api/admin/settings | Server-global settings: `{settings: {proxyForApp, proxyForAgent, proxyUrl}}` |
| PUT | /api/admin/settings | Update settings (fields optional; omitted fields keep their current value), returns the full updated settings |
`useSystemProxy` is the "use system HTTP proxy" switch (default on): while on, the server and its child processes honor HTTP_PROXY / HTTPS_PROXY / NO_PROXY (both spellings) for outbound traffic; while off, the server always connects directly and the proxy variables are stripped from agent command subprocess environments (NO_PROXY is kept). In either state the effective NO_PROXY always includes `localhost,127.0.0.1,::1` (loopback is never proxied). Toggling takes effect for newly initiated connections immediately — no restart.
The proxy settings are two independent switches sharing one optional explicit address; changes take effect for newly initiated connections/spawns immediately — no restart:
- `proxyForApp` ("application uses the proxy", default on) governs the server's own outbound traffic (LLM requests, the update check, image fetches): on with `proxyUrl` set → that address for both http and https, **taking precedence over the proxy environment variables** — no environment variable needs to be configured; on without an address → the environment variables HTTP_PROXY / HTTPS_PROXY / NO_PROXY (both spellings); off → always direct.
- `proxyForAgent` ("agent environment uses the proxy", default on) governs agent command subprocess environments: on with `proxyUrl` set → `HTTP_PROXY` / `HTTPS_PROXY` (plus lowercase twins) are injected as that address together with the merged NO_PROXY, overriding inherited values; on without an address → the host environment passes through unchanged; off → the proxy variables are stripped (NO_PROXY is kept).
- `proxyUrl` (default null = follow the environment variables) is the shared explicit address. Validation on PUT: the value is trimmed; empty or null clears the address; accepted forms are `http://host[:port]`, `https://host[:port]`, and bare `host[:port]` (normalized to `http://host[:port]` — only normalized values are stored, and the response echoes the stored form); anything else is `400` with code `invalid_proxy_url`, and the rejected PUT writes nothing.
In every on-state the effective NO_PROXY always includes `localhost,127.0.0.1,::1` (loopback is never proxied).
### Version and Self-Update
+8 -2
View File
@@ -73,10 +73,16 @@ curl -c cookies.txt -H "Content-Type: application/json" \
| 方法 | 路径 | 说明 |
| --- | --- | --- |
| GET | /api/admin/settings | 服务端全局设置:`{settings: {useSystemProxy}}` |
| GET | /api/admin/settings | 服务端全局设置:`{settings: {proxyForApp, proxyForAgent, proxyUrl}}` |
| PUT | /api/admin/settings | 更新设置(字段可省略,省略即保持现值),返回更新后的完整设置 |
`useSystemProxy` 即「使用系统 HTTP 代理」开关(默认开):开时服务端及其子进程出网遵循 HTTP_PROXY / HTTPS_PROXY / NO_PROXY(大小写并存);关时服务端一律直连,并从 Agent 命令子进程环境中剥除代理变量(NO_PROXY 保留)。任一状态下生效的 NO_PROXY 恒包含 `localhost,127.0.0.1,::1`(回环不代理)。切换即时生效(对新发起的连接),无需重启。
代理设置为两个独立开关共享一个可选的显式地址;修改即时生效(对新发起的连接与新派生的子进程),无需重启:
- `proxyForApp`(「应用程序使用代理」,默认开)治理服务端自身出网(LLM 请求、更新检查、图片抓取):开且填写了 `proxyUrl` → http 与 https 流量都走该地址,**优先于代理环境变量**——无需配置任何环境变量;开但未填地址 → 遵循 HTTP_PROXY / HTTPS_PROXY / NO_PROXY 环境变量(大小写并存);关 → 一律直连。
- `proxyForAgent`(「Agent 环境使用代理」,默认开)治理 Agent 命令子进程环境:开且填写了 `proxyUrl` → 注入 `HTTP_PROXY` / `HTTPS_PROXY`(含小写拼写)为该地址并附合并后的 NO_PROXY,覆盖继承值;开但未填地址 → 宿主环境原样透传;关 → 剥除代理变量(NO_PROXY 保留)。
- `proxyUrl`(默认 null = 跟随环境变量)即两者共享的显式地址。PUT 校验:先 trim;空串或 null 即清除地址;接受 `http://主机[:端口]`、`https://主机[:端口]` 与裸 `主机[:端口]`(规范化为 `http://主机[:端口]`——只存储规范化后的值,响应回显存储形态);其余一律 `400`,错误码 `invalid_proxy_url`,且被拒绝的 PUT 不写入任何字段。
任一开启状态下生效的 NO_PROXY 恒包含 `localhost,127.0.0.1,::1`(回环不代理)。
### 版本与在线更新