Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@ VITE_USE_LOCAL_PROXY=false
VITE_STACK_PROJECT_ID=dummy_project_id
VITE_STACK_PUBLISHABLE_CLIENT_KEY=dummy_publishable_key
VITE_STACK_SECRET_SERVER_KEY=dummy_secret_server_key

# HTTP proxy settings for local development
# HTTP_PROXY=http://127.0.0.1:9090
# HTTPS_PROXY=https://127.0.0.1:9090
# NO_PROXY=localhost,127.0.0.1
51 changes: 47 additions & 4 deletions electron/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import {
getEmailFolderPath,
getEnvPath,
maskProxyUrl,
readEnvValueWithPriority,
readGlobalEnvKey,
removeEnvKey,
updateEnvBlock,
Expand Down Expand Up @@ -138,11 +139,31 @@ app.commandLine.appendSwitch('enable-features', 'MemoryPressureReduction');
app.commandLine.appendSwitch('renderer-process-limit', '8');

// ==================== Proxy configuration ====================
// Read proxy from global .env file on startup
proxyUrl = readGlobalEnvKey('HTTP_PROXY');
// Read proxy from multiple sources with priority:
// 1. Process environment (inline: SET HTTP_PROXY=... && eigent.exe)
// 2. .env.development (development mode)
// 3. Global ~/.eigent/.env file
// Check both HTTP_PROXY and HTTPS_PROXY (with lowercase variants)
const httpProxy =
readEnvValueWithPriority('HTTP_PROXY') ||
readEnvValueWithPriority('http_proxy');
const httpsProxy =
readEnvValueWithPriority('HTTPS_PROXY') ||
readEnvValueWithPriority('https_proxy');

// Prefer HTTPS proxy if available, fallback to HTTP proxy
proxyUrl = httpsProxy || httpProxy;

if (proxyUrl) {
log.info(`[PROXY] Applying proxy configuration: ${maskProxyUrl(proxyUrl)}`);
app.commandLine.appendSwitch('proxy-server', proxyUrl);

// Log which proxy type is being used
if (httpsProxy) {
log.info('[PROXY] Using HTTPS_PROXY configuration');
} else if (httpProxy) {
log.info('[PROXY] Using HTTP_PROXY configuration');
}
} else {
log.info('[PROXY] No proxy configured');
}
Expand Down Expand Up @@ -1159,17 +1180,39 @@ function registerIpcHandlers() {
log.error('global env-remove error:', error);
}

// Also remove from .env.development file (remove from anywhere in file, not just MCP block)
const DEV_ENV_PATH = path.join(process.cwd(), '.env.development');
try {
if (fs.existsSync(DEV_ENV_PATH)) {
let devContent = fs.readFileSync(DEV_ENV_PATH, 'utf-8');
let devLines = devContent.split(/\r?\n/);
// Remove key from anywhere in the file (not limited to MCP block)
devLines = devLines.filter((line) => !line.trim().startsWith(key + '='));
fs.writeFileSync(DEV_ENV_PATH, devLines.join('\n'), 'utf-8');
log.info(`env-remove: removed ${key} from .env.development`);
}
} catch (error) {
log.error('.env.development env-remove error:', error);
}

return { success: true };
});

// ==================== read global env handler ====================
const ALLOWED_GLOBAL_ENV_KEYS = new Set(['HTTP_PROXY', 'HTTPS_PROXY']);
const ALLOWED_GLOBAL_ENV_KEYS = new Set([
'HTTP_PROXY',
'HTTPS_PROXY',
'NO_PROXY',
'http_proxy',
'https_proxy',
'no_proxy',
]);
ipcMain.handle('read-global-env', async (_event, key: string) => {
if (!ALLOWED_GLOBAL_ENV_KEYS.has(key)) {
log.warn(`[ENV] Blocked read of disallowed global env key: ${key}`);
return { value: null };
}
return { value: readGlobalEnvKey(key) };
return { value: readEnvValueWithPriority(key) };
});

// ==================== new window handler ====================
Expand Down
33 changes: 18 additions & 15 deletions electron/main/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import os from 'os';
import path from 'path';
import { promisify } from 'util';
import { PromiseReturnType } from './install-deps';
import { maskProxyUrl, readGlobalEnvKey } from './utils/envUtil';
import { maskProxyUrl, readEnvValueWithPriority } from './utils/envUtil';
import {
ensureTerminalVenvAtUserPath,
findNodejsWheelBinPath,
Expand All @@ -42,7 +42,10 @@ const execAsync = promisify(exec);

const DEFAULT_SERVER_URL = 'https://dev.eigent.ai/api';

function readEnvValue(filePath: string, key: string): string | undefined {
export function readEnvValue(
filePath: string,
key: string
): string | undefined {
try {
if (!fs.existsSync(filePath)) return undefined;
const content = fs.readFileSync(filePath, 'utf-8');
Expand Down Expand Up @@ -236,22 +239,22 @@ export async function startBackend(
const uvEnv = getUvEnv(currentVersion);
const globalEnvPath = path.join(os.homedir(), '.eigent', '.env');

// Load proxy configuration from global .env file
const proxyUrl = readGlobalEnvKey('HTTP_PROXY');

// Build proxy env vars if configured
const proxyEnv = proxyUrl
? {
HTTP_PROXY: proxyUrl,
HTTPS_PROXY: proxyUrl,
http_proxy: proxyUrl,
https_proxy: proxyUrl,
}
: {};
const proxyEnv = {
HTTP_PROXY: readEnvValueWithPriority('HTTP_PROXY'),
HTTPS_PROXY: readEnvValueWithPriority('HTTPS_PROXY'),
http_proxy: readEnvValueWithPriority('http_proxy'),
https_proxy: readEnvValueWithPriority('https_proxy'),
// Ensure local connections bypass proxy
NO_PROXY:
readEnvValueWithPriority('NO_PROXY') || 'localhost,127.0.0.1,.local',
no_proxy:
readEnvValueWithPriority('no_proxy') || 'localhost,127.0.0.1,.local',
};
Comment on lines -243 to +253
Copy link
Copy Markdown
Collaborator

@nitpicker55555 nitpicker55555 Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure why this change was made. The original code had a complete ternary condition:

proxyUrl ? { ... } : {}

When no proxy was provided, it returned an empty object and did not pollute the environment variables.

However, after this change, the ternary condition was removed and the return value of readEnvValueWithPriority() is written directly into the object. When there is no proxy, all four keys are null. Once passed to the child process, they become the string "null", which uv then treats as a proxy address during dependency installation, ultimately causing DNS resolution failures.

@bittoby @a7m-1st

This PR was reverted because it caused DNS resolution errors during dependency installation.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the code yesterday, but I didn't manage to catch it while testing, perhaps bcz uv install didn't trigger in my case. Apologies for the hassle.
Thanks for reverting it @nitpicker55555

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks @nitpicker55555 for pointing this out, could @bittoby or @a7m-1st reopen a PR and add additional fix, then we can review & merge this

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@a7m-1st @Wendong-Fan I get one fix pr
In #1226, I tested and is work

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi there, thanks @nitpicker55555. In addition to the enhancements I added, I think your PR would do.


if (proxyUrl) {
if (proxyEnv.HTTP_PROXY || proxyEnv.HTTPS_PROXY) {
log.info(
`[BACKEND] Proxy configured for backend: ${maskProxyUrl(proxyUrl)}`
`[BACKEND] Proxy configured for backend: ${maskProxyUrl((proxyEnv.HTTP_PROXY || proxyEnv.HTTPS_PROXY) as string)}`
);
}

Expand Down
29 changes: 29 additions & 0 deletions electron/main/utils/envUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import fs from 'fs';
import os from 'os';
import path from 'path';
import { readEnvValue } from '../init';

export const ENV_START = '# === MCP INTEGRATION ENV START ===';
export const ENV_END = '# === MCP INTEGRATION ENV END ===';
Expand Down Expand Up @@ -116,6 +117,34 @@ export function readGlobalEnvKey(key: string): string | null {
return null;
}

/**
* Read environment variable value with priority system.
*
* Priority order (highest to lowest):
* 1. Process environment variables (inline/system)
* 2. .env.development file (development mode only)
* 3. Global ~/.eigent/.env file
*
* @param key - The environment variable key to read
* @returns The value if found, null otherwise
*/
export function readEnvValueWithPriority(key: string): string | null {
// Priority 1: Process environment variables (highest priority)
if (process.env[key]) {
return process.env[key]!;
}

// Priority 2: .env.development file (development mode only)
if (process.env.NODE_ENV === 'development') {
const devEnvPath = path.join(process.cwd(), '.env.development');
const value = readEnvValue(devEnvPath, key);
if (value) return value;
}

// Priority 3: Global ~/.eigent/.env file
return readGlobalEnvKey(key);
}

/**
* Mask credentials in a proxy URL for safe logging.
* e.g. "http://user:pass@host:port" → "http://***:***@host:port"
Expand Down
73 changes: 72 additions & 1 deletion electron/main/utils/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import log from 'electron-log';
import fs from 'fs';
import os from 'os';
import path from 'path';
import { maskProxyUrl, readEnvValueWithPriority } from './envUtil';

export function getResourcePath() {
return path.join(app.getAppPath(), 'resources');
Expand All @@ -33,6 +34,69 @@ export function getBackendPath() {
}
}

/**
* Get proxy environment variables with priority:
* 1. Process environment variables (inline/system)
* 2. .env.development file (development mode only)
* 3. Global ~/.eigent/.env config
*
* Returns an object with HTTP_PROXY, HTTPS_PROXY, NO_PROXY and lowercase variants
* if a proxy is configured, or an empty object if not.
* Supports separate HTTP and HTTPS proxy configurations.
*/
function getProxyEnvVars(): Record<string, string> {
// Check both uppercase and lowercase variants
const httpProxy =
readEnvValueWithPriority('HTTP_PROXY') ||
readEnvValueWithPriority('http_proxy');

const httpsProxy =
readEnvValueWithPriority('HTTPS_PROXY') ||
readEnvValueWithPriority('https_proxy');

// Return empty object if no proxy configured
if (!httpProxy && !httpsProxy) {
return {};
}

// Log configured proxies
if (httpProxy) {
log.info(
`[INSTALL SCRIPT] HTTP Proxy configured: ${maskProxyUrl(httpProxy)}`
);
}
if (httpsProxy) {
log.info(
`[INSTALL SCRIPT] HTTPS Proxy configured: ${maskProxyUrl(httpsProxy)}`
);
}

// Get NO_PROXY configuration (with default for local connections)
const noProxy = readEnvValueWithPriority('NO_PROXY') ||
readEnvValueWithPriority('no_proxy') ||
'localhost,127.0.0.1,.local';

// Return all variants (some tools need uppercase, others lowercase)
// Filter out undefined values
const result: Record<string, string> = {};

if (httpProxy) {
result.HTTP_PROXY = httpProxy;
result.http_proxy = httpProxy;
}

if (httpsProxy) {
result.HTTPS_PROXY = httpsProxy;
result.https_proxy = httpsProxy;
}

// Always set NO_PROXY when proxy is configured to avoid issues with local connections
result.NO_PROXY = noProxy;
result.no_proxy = noProxy;

return result;
Comment on lines +81 to +97
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lastly I think we can simplify this logic drastically by returning:
{ HTTPS_PROXY: httpsProxy, HTTP_PROXY: httpProxy }

}

export function runInstallScript(scriptPath: string): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => {
const installScriptPath = path.join(
Expand All @@ -42,8 +106,15 @@ export function runInstallScript(scriptPath: string): Promise<boolean> {
);
log.info(`Running script at: ${installScriptPath}`);

// Get proxy configuration from global .env file
const proxyEnv = getProxyEnvVars();

const nodeProcess = spawn(process.execPath, [installScriptPath], {
env: { ...process.env, ELECTRON_RUN_AS_NODE: '1' },
env: {
...process.env,
...proxyEnv,
ELECTRON_RUN_AS_NODE: '1',
},
});

let stderrOutput = '';
Expand Down
Loading