@@ -11,11 +11,12 @@ export type ResolvedProxy = {
1111
1212/**
1313 * Determine whether a target URL should bypass the proxy based on NO_PROXY.
14+ * 根据 NO_PROXY 判断目标 URL 是否应绕过代理。
1415 *
15- * Supports:
16- * - `*` wildcard (bypass everything)
17- * - Exact hostname match (e.g. `localhost`, `example.com`)
18- * - Sub-domain wildcard (e.g. `.example.com` matches `api.example.com`)
16+ * Supports / 支持:
17+ * - `*` wildcard (bypass everything) / 通配符(绕过所有地址)
18+ * - Exact hostname match (e.g. `localhost`, `example.com`) / 精确主机名匹配
19+ * - Sub-domain wildcard (e.g. `.example.com` matches `api.example.com`) / 子域名通配
1920 */
2021function shouldBypassProxy ( targetUrl : string , noProxy : string ) : boolean {
2122 if ( ! noProxy . trim ( ) ) {
@@ -41,6 +42,7 @@ function shouldBypassProxy(targetUrl: string, noProxy: string): boolean {
4142 for ( const entry of entries ) {
4243 if ( entry . startsWith ( "." ) ) {
4344 // ".example.com" matches "api.example.com" and "example.com"
45+ // ".example.com" 同时匹配 "api.example.com" 和 "example.com"
4446 if ( hostname . endsWith ( entry ) || hostname === entry . slice ( 1 ) ) {
4547 return true ;
4648 }
@@ -56,6 +58,9 @@ function shouldBypassProxy(targetUrl: string, noProxy: string): boolean {
5658 * Pick the first non-empty proxy variable from `source`, preferring
5759 * HTTPS_PROXY → HTTP_PROXY → SOCKS_PROXY → SOCKS5_PROXY (case-insensitive
5860 * lowercase fallback for each).
61+ *
62+ * 从 `source` 中取第一个非空的代理变量,优先级:
63+ * HTTPS_PROXY → HTTP_PROXY → SOCKS_PROXY → SOCKS5_PROXY(大小写均兼容)。
5964 */
6065function pickProxyVar ( source : Record < string , string | undefined > ) : { url : string ; type : ProxyType } | undefined {
6166 const candidates : Array < { keys : string [ ] ; type : ProxyType } > = [
@@ -78,6 +83,7 @@ function pickProxyVar(source: Record<string, string | undefined>): { url: string
7883
7984/**
8085 * Pick NO_PROXY from a source (case-insensitive fallback).
86+ * 从 source 中读取 NO_PROXY(兼容小写 no_proxy)。
8187 */
8288function pickNoProxy ( source : Record < string , string | undefined > ) : string {
8389 return (
@@ -94,19 +100,28 @@ function pickNoProxy(source: Record<string, string | undefined>): string {
94100 * 3. Process environment variables (both standard `HTTP_PROXY` / `HTTPS_PROXY`
95101 * and `DEEPCODE_`-prefixed variants)
96102 *
103+ * 按以下优先级解析有效代理地址(由低到高):
104+ * 1. 用户级 `settings.json` → `env`
105+ * 2. 项目级 `settings.json` → `env`
106+ * 3. 进程环境变量(标准 `HTTP_PROXY` / `HTTPS_PROXY` 及 `DEEPCODE_` 前缀变体)
107+ *
97108 * Returns `undefined` when no proxy is configured or when NO_PROXY matches.
109+ * 未配置代理或命中 NO_PROXY 时返回 `undefined`。
98110 */
99111export function resolveProxyUrl (
100112 targetUrl : string ,
101113 projectRoot : string = process . cwd ( ) ,
102114 processEnv : SettingsProcessEnv = process . env
103115) : ResolvedProxy | undefined {
104116 // --- Collect proxy vars from each layer ---
117+ // --- 从各配置层收集代理变量 ---
105118 const userEnv = readSettings ( ) ?. env ?? { } ;
106119 const projectEnv = readProjectSettings ( projectRoot ) ?. env ?? { } ;
107120
108121 // System env includes both standard proxy vars and DEEPCODE_-prefixed ones
109122 // (collectDeepcodeEnv strips the prefix, so DEEPCODE_HTTPS_PROXY → HTTPS_PROXY).
123+ // 系统环境变量同时包含标准代理变量和 DEEPCODE_ 前缀变量
124+ // (collectDeepcodeEnv 会去除前缀,因此 DEEPCODE_HTTPS_PROXY → HTTPS_PROXY)。
110125 const systemProxySource : Record < string , string | undefined > = { ...processEnv } ;
111126 for ( const [ key , value ] of Object . entries ( processEnv ) ) {
112127 if ( key . startsWith ( "DEEPCODE_" ) && typeof value === "string" ) {
@@ -118,19 +133,22 @@ export function resolveProxyUrl(
118133 }
119134
120135 // --- NO_PROXY check (system level takes absolute precedence) ---
136+ // --- NO_PROXY 检查(系统级拥有绝对优先权)---
121137 const systemNoProxy = pickNoProxy ( systemProxySource ) ;
122138 if ( shouldBypassProxy ( targetUrl , systemNoProxy ) ) {
123139 return undefined ;
124140 }
125141
126142 // --- Merge: user < project < system ---
143+ // --- 合并优先级:用户 < 项目 < 系统 ---
127144 const merged : Record < string , string | undefined > = {
128145 ...userEnv ,
129146 ...projectEnv ,
130147 ...systemProxySource ,
131148 } ;
132149
133150 // NO_PROXY from merged (user/project may also define it, but system wins)
151+ // 合并后的 NO_PROXY(用户/项目也可定义,但系统级优先)
134152 const mergedNoProxy = pickNoProxy ( merged ) ;
135153 if ( shouldBypassProxy ( targetUrl , mergedNoProxy ) ) {
136154 return undefined ;
@@ -141,13 +159,16 @@ export function resolveProxyUrl(
141159
142160// ---------------------------------------------------------------------------
143161// Dispatcher cache – avoids re-creating ProxyAgent on every request.
162+ // Dispatcher 缓存 —— 避免每次请求都重建 ProxyAgent。
144163// ---------------------------------------------------------------------------
145164let cachedProxyUrl = "" ;
146165let cachedDispatcher : ProxyAgent | null = null ;
147166
148167/**
149168 * Return a `ProxyAgent` dispatcher when a proxy is configured for the given
150169 * target URL, or `null` when requests should go direct.
170+ *
171+ * 当目标 URL 命中代理配置时返回 `ProxyAgent` dispatcher,否则返回 `null` 直连。
151172 */
152173export function getProxyDispatcher ( targetUrl ?: string ) : ProxyAgent | null {
153174 const resolved = resolveProxyUrl ( targetUrl ?? "https://api.deepseek.com" ) ;
@@ -175,6 +196,9 @@ export function getProxyDispatcher(targetUrl?: string): ProxyAgent | null {
175196 * Fetch wrapper that automatically routes through the configured proxy.
176197 * Use this in place of the global `fetch` for any HTTP request that should
177198 * respect the proxy configuration.
199+ *
200+ * 自动走代理的 fetch 封装。
201+ * 任何需要遵循代理配置的 HTTP 请求均应使用此函数替代全局 `fetch`。
178202 */
179203export async function proxyFetch ( url : string | URL , init ?: RequestInit ) : Promise < Response > {
180204 const dispatcher = getProxyDispatcher ( String ( url ) ) ;
0 commit comments