背景 链接到标题

Pi Coding Agent 统一通过 HTTP_PROXY 环境变量控制所有 fetch 请求(LLM API 调用 + 工具层请求)是否走代理。代理服务器自身配置例外规则(如国内地址直连),deepseek 等国内服务实际不绕路。

配置方式:在 config.json 中写入代理地址,项目持久化,重启后自动生效。

SDK 网络层 链接到标题

Pi Coding Agent 用 undici.setGlobalDispatcher() 替换全局 fetch,底层由 undici.EnvHttpProxyAgent 控制。该 dispatcher 同时服务于 LLM API 请求和工具 execute 中的 fetchHTTP_PROXY / HTTPS_PROXY 环境变量同时影响两端。

AgentSession
    ├── LLM API 请求(OpenAI / Anthropic / DeepSeek)
    └── 工具 execute fetch(search / fetch / curl 等)
              │
              ▼
        undici 全局 dispatcher
              │
              ▼
        EnvHttpProxyAgent(读取 HTTP_PROXY)
              │
              ▼
            代理服务器
          (自己做例外规则)

配置方式 链接到标题

config.json 新增 httpProxy 链接到标题

{
  "provider": "deepseek",
  "model": "deepseek-v4-flash",
  "apiKey": "sk-xxx",
  "cwd": "~/project",
  "httpProxy": "http://127.0.0.1:7890",
  "windmillUrl": "http://ape:3900",
  "windmillToken": ""
}

留空则不设代理。

main.js 读取并写入环境变量 链接到标题

// 在 session:create 中,auth 设置之后
const proxy = config.httpProxy?.trim();
if (proxy) {
  process.env.HTTP_PROXY ??= proxy;
  process.env.HTTPS_PROXY ??= proxy;
}

??= 不覆盖已有的环境变量,启动前手动 export HTTP_PROXY=... 仍有效。

UI 配置入口 链接到标题

初次配置表单和重连弹窗均增加 HTTP Proxy 输入框:

初次配置:  [API Key] [Provider] [Model] [工作目录] [HTTP Proxy] [Windmill URL] [Windmill Token]
重连弹窗:  同上

输入后保存并重连,配置写入 config.json,下次启动自动读取。

工具层 fetch 自动走代理 链接到标题

扩展 execute 函数中直接用 fetch,无需额外配置:

async execute(_toolCallId, params, signal) {
  const resp = await fetch("https://www.google.com/search", {
    method: "GET",
    signal,
  });
  const data = await resp.json();
  return {
    content: [{ type: "text", text: JSON.stringify(data) }],
    details: {},
  };
}

fetch 使用全局 dispatcher,HTTP_PROXY 自动生效。如需工具单独走不同代理,可手动创建 https-proxy-agent 透传给 fetch

踩坑 链接到标题

applyHttpProxySettings 不在包导出路径 链接到标题

SDK 内部有 applyHttpProxySettings 函数,但 package.jsonexports 字段只开放 ../rpc-entrydist/core/http-dispatcher.js 无法直接 import

// ❌ 报错:Package subpath ... is not defined by "exports"
const { applyHttpProxySettings } = await import('@earendil-works/pi-coding-agent/dist/core/http-dispatcher.js');

解决:直接内联,process.env.HTTP_PROXY ??= proxy,行为与 applyHttpProxySettings 一致。

ipcMain.handle 在 session:create 内重复注册 链接到标题

ipcMain.handle('ui:response') 放在 session:create 内部,每次重连(如修改代理后保存)会再次注册同一 channel,报 Attempted to register a second handler

解决:pendingUI map 和 ipcMain.handle('ui:response') 提升到模块级别,只注册一次。session:create 内只 pendingUI = new Map() 重置。

参考 链接到标题