fix(gateway): prevent reconnect race and globally hide windows subprocess console
This commit is contained in:
@@ -106,6 +106,7 @@ export class ClawHubService {
|
|||||||
...env,
|
...env,
|
||||||
CLAWHUB_WORKDIR: this.workDir,
|
CLAWHUB_WORKDIR: this.workDir,
|
||||||
},
|
},
|
||||||
|
windowsHide: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
let stdout = '';
|
let stdout = '';
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ import {
|
|||||||
getReconnectSkipReason,
|
getReconnectSkipReason,
|
||||||
isLifecycleSuperseded,
|
isLifecycleSuperseded,
|
||||||
nextLifecycleEpoch,
|
nextLifecycleEpoch,
|
||||||
shouldHideConsoleWindow,
|
|
||||||
} from './process-policy';
|
} from './process-policy';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -709,7 +708,7 @@ export class GatewayManager extends EventEmitter {
|
|||||||
|
|
||||||
const { stdout } = await new Promise<{ stdout: string }>((resolve, reject) => {
|
const { stdout } = await new Promise<{ stdout: string }>((resolve, reject) => {
|
||||||
import('child_process').then(cp => {
|
import('child_process').then(cp => {
|
||||||
cp.exec(cmd, { timeout: 5000, windowsHide: shouldHideConsoleWindow() }, (err, stdout) => {
|
cp.exec(cmd, { timeout: 5000, windowsHide: true }, (err, stdout) => {
|
||||||
if (err) resolve({ stdout: '' });
|
if (err) resolve({ stdout: '' });
|
||||||
else resolve({ stdout });
|
else resolve({ stdout });
|
||||||
});
|
});
|
||||||
@@ -739,7 +738,7 @@ export class GatewayManager extends EventEmitter {
|
|||||||
import('child_process').then(cp => {
|
import('child_process').then(cp => {
|
||||||
cp.exec(
|
cp.exec(
|
||||||
`taskkill /PID ${pid} /T /F`,
|
`taskkill /PID ${pid} /T /F`,
|
||||||
{ timeout: 5000, windowsHide: shouldHideConsoleWindow() },
|
{ timeout: 5000, windowsHide: true },
|
||||||
() => { }
|
() => { }
|
||||||
);
|
);
|
||||||
}).catch(() => { });
|
}).catch(() => { });
|
||||||
@@ -844,7 +843,7 @@ export class GatewayManager extends EventEmitter {
|
|||||||
stdio: ['ignore', 'pipe', 'pipe'],
|
stdio: ['ignore', 'pipe', 'pipe'],
|
||||||
detached: false,
|
detached: false,
|
||||||
shell: false,
|
shell: false,
|
||||||
windowsHide: shouldHideConsoleWindow(),
|
windowsHide: true,
|
||||||
env: spawnEnv,
|
env: spawnEnv,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1098,7 +1097,7 @@ export class GatewayManager extends EventEmitter {
|
|||||||
stdio: ['ignore', 'pipe', 'pipe'],
|
stdio: ['ignore', 'pipe', 'pipe'],
|
||||||
detached: false,
|
detached: false,
|
||||||
shell: useShell,
|
shell: useShell,
|
||||||
windowsHide: shouldHideConsoleWindow(),
|
windowsHide: true,
|
||||||
env: spawnEnv,
|
env: spawnEnv,
|
||||||
});
|
});
|
||||||
const child = this.process;
|
const child = this.process;
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
export function shouldHideConsoleWindow(platform: NodeJS.Platform = process.platform): boolean {
|
|
||||||
return platform === 'win32';
|
|
||||||
}
|
|
||||||
|
|
||||||
export function nextLifecycleEpoch(currentEpoch: number): number {
|
export function nextLifecycleEpoch(currentEpoch: number): number {
|
||||||
return currentEpoch + 1;
|
return currentEpoch + 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -526,6 +526,7 @@ export async function validateChannelConfig(channelType: string): Promise<Valida
|
|||||||
cwd: openclawPath,
|
cwd: openclawPath,
|
||||||
encoding: 'utf-8',
|
encoding: 'utf-8',
|
||||||
timeout: 30000,
|
timeout: 30000,
|
||||||
|
windowsHide: true,
|
||||||
},
|
},
|
||||||
(err, stdout) => {
|
(err, stdout) => {
|
||||||
if (err) reject(err);
|
if (err) reject(err);
|
||||||
|
|||||||
@@ -269,6 +269,7 @@ export function generateCompletionCache(): void {
|
|||||||
},
|
},
|
||||||
stdio: 'ignore',
|
stdio: 'ignore',
|
||||||
detached: false,
|
detached: false,
|
||||||
|
windowsHide: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
child.on('close', (code) => {
|
child.on('close', (code) => {
|
||||||
@@ -305,7 +306,8 @@ export function installCompletionToProfile(): void {
|
|||||||
},
|
},
|
||||||
stdio: 'ignore',
|
stdio: 'ignore',
|
||||||
detached: false,
|
detached: false,
|
||||||
},
|
windowsHide: true,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
child.on('close', (code) => {
|
child.on('close', (code) => {
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ function resolveUvBin(): { bin: string; source: 'bundled' | 'path' | 'bundled-fa
|
|||||||
function findUvInPathSync(): boolean {
|
function findUvInPathSync(): boolean {
|
||||||
try {
|
try {
|
||||||
const cmd = process.platform === 'win32' ? 'where.exe uv' : 'which uv';
|
const cmd = process.platform === 'win32' ? 'where.exe uv' : 'which uv';
|
||||||
execSync(cmd, { stdio: 'ignore', timeout: 5000 });
|
execSync(cmd, { stdio: 'ignore', timeout: 5000, windowsHide: true });
|
||||||
return true;
|
return true;
|
||||||
} catch {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
@@ -95,6 +95,7 @@ export async function isPythonReady(): Promise<boolean> {
|
|||||||
try {
|
try {
|
||||||
const child = spawn(useShell ? quoteForCmd(uvBin) : uvBin, ['python', 'find', '3.12'], {
|
const child = spawn(useShell ? quoteForCmd(uvBin) : uvBin, ['python', 'find', '3.12'], {
|
||||||
shell: useShell,
|
shell: useShell,
|
||||||
|
windowsHide: true,
|
||||||
});
|
});
|
||||||
child.on('close', (code) => resolve(code === 0));
|
child.on('close', (code) => resolve(code === 0));
|
||||||
child.on('error', () => resolve(false));
|
child.on('error', () => resolve(false));
|
||||||
@@ -121,6 +122,7 @@ async function runPythonInstall(
|
|||||||
const child = spawn(useShell ? quoteForCmd(uvBin) : uvBin, ['python', 'install', '3.12'], {
|
const child = spawn(useShell ? quoteForCmd(uvBin) : uvBin, ['python', 'install', '3.12'], {
|
||||||
shell: useShell,
|
shell: useShell,
|
||||||
env,
|
env,
|
||||||
|
windowsHide: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
child.stdout?.on('data', (data) => {
|
child.stdout?.on('data', (data) => {
|
||||||
@@ -210,6 +212,7 @@ export async function setupManagedPython(): Promise<void> {
|
|||||||
const child = spawn(verifyShell ? quoteForCmd(uvBin) : uvBin, ['python', 'find', '3.12'], {
|
const child = spawn(verifyShell ? quoteForCmd(uvBin) : uvBin, ['python', 'find', '3.12'], {
|
||||||
shell: verifyShell,
|
shell: verifyShell,
|
||||||
env: { ...process.env, ...uvEnv },
|
env: { ...process.env, ...uvEnv },
|
||||||
|
windowsHide: true,
|
||||||
});
|
});
|
||||||
let output = '';
|
let output = '';
|
||||||
child.stdout?.on('data', (data) => { output += data; });
|
child.stdout?.on('data', (data) => { output += data; });
|
||||||
|
|||||||
@@ -3,21 +3,9 @@ import {
|
|||||||
getReconnectSkipReason,
|
getReconnectSkipReason,
|
||||||
isLifecycleSuperseded,
|
isLifecycleSuperseded,
|
||||||
nextLifecycleEpoch,
|
nextLifecycleEpoch,
|
||||||
shouldHideConsoleWindow,
|
|
||||||
} from '@electron/gateway/process-policy';
|
} from '@electron/gateway/process-policy';
|
||||||
|
|
||||||
describe('gateway process policy helpers', () => {
|
describe('gateway process policy helpers', () => {
|
||||||
describe('shouldHideConsoleWindow', () => {
|
|
||||||
it('returns true on Windows', () => {
|
|
||||||
expect(shouldHideConsoleWindow('win32')).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns false on non-Windows platforms', () => {
|
|
||||||
expect(shouldHideConsoleWindow('darwin')).toBe(false);
|
|
||||||
expect(shouldHideConsoleWindow('linux')).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('lifecycle epoch helpers', () => {
|
describe('lifecycle epoch helpers', () => {
|
||||||
it('increments lifecycle epoch by one', () => {
|
it('increments lifecycle epoch by one', () => {
|
||||||
expect(nextLifecycleEpoch(0)).toBe(1);
|
expect(nextLifecycleEpoch(0)).toBe(1);
|
||||||
|
|||||||
Reference in New Issue
Block a user