fix two clawx instances bug (#294)

This commit is contained in:
paisley
2026-03-04 21:20:21 +08:00
committed by GitHub
Unverified
parent fd58e721bd
commit 5ddb7d281d
2 changed files with 52 additions and 13 deletions

View File

@@ -126,28 +126,49 @@ const GATEWAY_FETCH_PRELOAD_SOURCE = `'use strict';
return _f.call(globalThis, input, init); return _f.call(globalThis, input, init);
}; };
// Global monkey-patch for child_process to enforce windowsHide: true on Windows // Global monkey-patch for child_process to enforce windowsHide: true on Windows.
// This prevents OpenClaw's tools (e.g. Terminal, Python) from flashing black // This prevents OpenClaw's tools (e.g. Terminal, Python) from flashing black
// command boxes during AI conversations, without triggering AVs. // command boxes during AI conversations, without triggering AVs.
//
// Node child_process signatures vary:
// spawn(cmd[, args][, options])
// exec(cmd[, options][, callback])
// execFile(file[, args][, options][, callback])
// *Sync variants omit the callback
//
// Strategy: scan arguments for the first plain-object (the options param).
// If found, set windowsHide on it. If absent, insert a new options object
// before any trailing callback so the signature stays valid.
if (process.platform === 'win32') { if (process.platform === 'win32') {
try { try {
var cp = require('child_process'); var cp = require('child_process');
if (!cp.__clawxPatched) { if (!cp.__clawxPatched) {
cp.__clawxPatched = true; cp.__clawxPatched = true;
['spawn', 'exec', 'execFile', 'spawnSync', 'execSync', 'execFileSync'].forEach(function(method) { ['spawn', 'exec', 'execFile', 'fork', 'spawnSync', 'execSync', 'execFileSync'].forEach(function(method) {
var original = cp[method]; var original = cp[method];
if (typeof original === 'function') { if (typeof original !== 'function') return;
cp[method] = function() { cp[method] = function() {
var args = Array.prototype.slice.call(arguments); var args = Array.prototype.slice.call(arguments);
var lastArg = args[args.length - 1]; var optIdx = -1;
if (lastArg && typeof lastArg === 'object' && !Array.isArray(lastArg)) { for (var i = 1; i < args.length; i++) {
lastArg.windowsHide = true; var a = args[i];
if (a && typeof a === 'object' && !Array.isArray(a)) {
optIdx = i;
break;
}
}
if (optIdx >= 0) {
args[optIdx].windowsHide = true;
} else { } else {
args.push({ windowsHide: true }); var opts = { windowsHide: true };
if (typeof args[args.length - 1] === 'function') {
args.splice(args.length - 1, 0, opts);
} else {
args.push(opts);
}
} }
return original.apply(this, args); return original.apply(this, args);
}; };
}
}); });
} }
} catch (e) { } catch (e) {

View File

@@ -37,6 +37,15 @@ import { ensureBuiltinSkillsInstalled } from '../utils/skill-config';
// set `"disable-hardware-acceleration": false` in the app config (future). // set `"disable-hardware-acceleration": false` in the app config (future).
app.disableHardwareAcceleration(); app.disableHardwareAcceleration();
// Prevent multiple instances of the app from running simultaneously.
// Without this, two instances each spawn their own gateway process on the
// same port, then each treats the other's gateway as "orphaned" and kills
// it — creating an infinite kill/restart loop on Windows.
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) {
app.quit();
}
// Global references // Global references
let mainWindow: BrowserWindow | null = null; let mainWindow: BrowserWindow | null = null;
const gatewayManager = new GatewayManager(); const gatewayManager = new GatewayManager();
@@ -242,6 +251,15 @@ async function initialize(): Promise<void> {
}); });
} }
// When a second instance is launched, focus the existing window instead.
app.on('second-instance', () => {
if (mainWindow) {
if (mainWindow.isMinimized()) mainWindow.restore();
mainWindow.show();
mainWindow.focus();
}
});
// Application lifecycle // Application lifecycle
app.whenReady().then(() => { app.whenReady().then(() => {
initialize(); initialize();