fix: persist uv mirror config file (#921)
This commit is contained in:
@@ -1,12 +1,52 @@
|
||||
import { app } from 'electron';
|
||||
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
||||
import { request } from 'https';
|
||||
import path from 'path';
|
||||
import { logger } from './logger';
|
||||
import { getOpenClawConfigDir } from './paths';
|
||||
|
||||
const UV_PYTHON_INSTALL_MIRROR_URL = 'https://registry.npmmirror.com/-/binary/python-build-standalone/';
|
||||
const UV_INDEX_URL = 'https://pypi.tuna.tsinghua.edu.cn/simple/';
|
||||
|
||||
const UV_MIRROR_ENV: Record<string, string> = {
|
||||
UV_PYTHON_INSTALL_MIRROR: 'https://registry.npmmirror.com/-/binary/python-build-standalone/',
|
||||
UV_INDEX_URL: 'https://pypi.tuna.tsinghua.edu.cn/simple/',
|
||||
UV_PYTHON_INSTALL_MIRROR: UV_PYTHON_INSTALL_MIRROR_URL,
|
||||
UV_INDEX_URL,
|
||||
};
|
||||
|
||||
function quoteTomlString(value: string): string {
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
|
||||
function buildClawXUvConfigToml(): string {
|
||||
return [
|
||||
'# This file is managed by ClawX.',
|
||||
'# It lets uv use ClawX-selected mirrors even when host exec filters UV_INDEX_URL.',
|
||||
`index-url = ${quoteTomlString(UV_INDEX_URL)}`,
|
||||
`python-install-mirror = ${quoteTomlString(UV_PYTHON_INSTALL_MIRROR_URL)}`,
|
||||
'',
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
export function getClawXUvConfigFilePath(): string {
|
||||
return path.join(getOpenClawConfigDir(), 'clawx', 'uv.toml');
|
||||
}
|
||||
|
||||
function ensureClawXUvConfigFile(): string | null {
|
||||
const filePath = getClawXUvConfigFilePath();
|
||||
const content = buildClawXUvConfigToml();
|
||||
|
||||
try {
|
||||
mkdirSync(path.dirname(filePath), { recursive: true });
|
||||
if (!existsSync(filePath) || readFileSync(filePath, 'utf-8') !== content) {
|
||||
writeFileSync(filePath, content, { encoding: 'utf-8', mode: 0o644 });
|
||||
}
|
||||
return filePath;
|
||||
} catch (err) {
|
||||
logger.warn('Failed to write ClawX uv config file:', err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const GOOGLE_204_HOST = 'www.google.com';
|
||||
const GOOGLE_204_PATH = '/generate_204';
|
||||
const GOOGLE_204_TIMEOUT_MS = 2000;
|
||||
@@ -108,7 +148,12 @@ export async function shouldOptimizeNetwork(): Promise<boolean> {
|
||||
|
||||
export async function getUvMirrorEnv(): Promise<Record<string, string>> {
|
||||
const isOptimized = await shouldOptimizeNetwork();
|
||||
return isOptimized ? { ...UV_MIRROR_ENV } : {};
|
||||
if (!isOptimized) return {};
|
||||
|
||||
const uvConfigFile = ensureClawXUvConfigFile();
|
||||
return uvConfigFile
|
||||
? { ...UV_MIRROR_ENV, UV_CONFIG_FILE: uvConfigFile }
|
||||
: { ...UV_MIRROR_ENV };
|
||||
}
|
||||
|
||||
export async function warmupNetworkOptimization(): Promise<void> {
|
||||
|
||||
60
tests/unit/uv-env.test.ts
Normal file
60
tests/unit/uv-env.test.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
// @vitest-environment node
|
||||
import { existsSync, mkdtempSync, readFileSync, rmSync } from 'fs';
|
||||
import { tmpdir } from 'os';
|
||||
import path from 'path';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
let tempHome: string;
|
||||
|
||||
beforeEach(() => {
|
||||
tempHome = mkdtempSync(path.join(tmpdir(), 'clawx-uv-env-'));
|
||||
vi.resetModules();
|
||||
|
||||
vi.doMock('os', async () => {
|
||||
const actual = await vi.importActual<typeof import('os')>('os');
|
||||
return {
|
||||
...actual,
|
||||
homedir: () => tempHome,
|
||||
};
|
||||
});
|
||||
|
||||
vi.doMock('electron', () => ({
|
||||
app: {
|
||||
getLocale: vi.fn().mockReturnValue('en-US'),
|
||||
getPath: vi.fn().mockReturnValue(path.join(tempHome, '.clawx')),
|
||||
getAppPath: vi.fn().mockReturnValue(process.cwd()),
|
||||
getVersion: vi.fn().mockReturnValue('0.0.0-test'),
|
||||
isPackaged: false,
|
||||
isReady: vi.fn().mockReturnValue(true),
|
||||
whenReady: vi.fn().mockResolvedValue(undefined),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.spyOn(Intl, 'DateTimeFormat').mockImplementation(() => ({
|
||||
resolvedOptions: () => ({ timeZone: 'Asia/Shanghai' }),
|
||||
}) as Intl.DateTimeFormat);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
vi.resetModules();
|
||||
if (tempHome) {
|
||||
rmSync(tempHome, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
describe('uv mirror environment', () => {
|
||||
it('writes a ClawX-managed uv.toml under ~/.openclaw and exposes it via UV_CONFIG_FILE', async () => {
|
||||
const { getUvMirrorEnv } = await import('@electron/utils/uv-env');
|
||||
|
||||
const env = await getUvMirrorEnv();
|
||||
|
||||
const expectedPath = path.join(tempHome, '.openclaw', 'clawx', 'uv.toml');
|
||||
expect(env.UV_INDEX_URL).toBe('https://pypi.tuna.tsinghua.edu.cn/simple/');
|
||||
expect(env.UV_PYTHON_INSTALL_MIRROR).toBe('https://registry.npmmirror.com/-/binary/python-build-standalone/');
|
||||
expect(env.UV_CONFIG_FILE).toBe(expectedPath);
|
||||
expect(existsSync(expectedPath)).toBe(true);
|
||||
expect(readFileSync(expectedPath, 'utf-8')).toContain('index-url = "https://pypi.tuna.tsinghua.edu.cn/simple/"');
|
||||
expect(readFileSync(expectedPath, 'utf-8')).toContain('python-install-mirror = "https://registry.npmmirror.com/-/binary/python-build-standalone/"');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user