feat: prepare Zhinian desktop client for pilot release
This commit is contained in:
@@ -48,7 +48,8 @@ import { browserOAuthManager } from '../utils/browser-oauth';
|
||||
import { whatsAppLoginManager } from '../utils/whatsapp-login';
|
||||
import { syncAllProviderAuthToRuntime } from '../services/providers/provider-runtime-sync';
|
||||
|
||||
const WINDOWS_APP_USER_MODEL_ID = 'app.clawx.desktop';
|
||||
const WINDOWS_APP_USER_MODEL_ID = 'app.zhinian.assistant';
|
||||
const PRODUCT_NAME = '智念助手';
|
||||
const isE2EMode = process.env.CLAWX_E2E === '1';
|
||||
const requestedUserDataDir = process.env.CLAWX_USER_DATA_DIR?.trim();
|
||||
|
||||
@@ -56,6 +57,8 @@ if (isE2EMode && requestedUserDataDir) {
|
||||
app.setPath('userData', requestedUserDataDir);
|
||||
}
|
||||
|
||||
app.setName(PRODUCT_NAME);
|
||||
|
||||
// Disable GPU hardware acceleration globally for maximum stability across
|
||||
// all GPU configurations (no GPU, integrated, discrete).
|
||||
//
|
||||
@@ -77,7 +80,7 @@ app.disableHardwareAcceleration();
|
||||
// on X11 it supplements the StartupWMClass matching.
|
||||
// Must be called before app.whenReady() / before any window is created.
|
||||
if (process.platform === 'linux') {
|
||||
app.setDesktopName('clawx.desktop');
|
||||
app.setDesktopName('zhinian-assistant.desktop');
|
||||
}
|
||||
|
||||
// Prevent multiple instances of the app from running simultaneously.
|
||||
@@ -139,21 +142,29 @@ function getIconsDir(): string {
|
||||
return join(__dirname, '../../resources/icons');
|
||||
}
|
||||
|
||||
function getAppIconPath(): string {
|
||||
const iconsDir = getIconsDir();
|
||||
return process.platform === 'win32'
|
||||
? join(iconsDir, 'icon.ico')
|
||||
: join(iconsDir, 'icon.png');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the app icon for the current platform
|
||||
*/
|
||||
function getAppIcon(): Electron.NativeImage | undefined {
|
||||
if (process.platform === 'darwin') return undefined; // macOS uses the app bundle icon
|
||||
|
||||
const iconsDir = getIconsDir();
|
||||
const iconPath =
|
||||
process.platform === 'win32'
|
||||
? join(iconsDir, 'icon.ico')
|
||||
: join(iconsDir, 'icon.png');
|
||||
const icon = nativeImage.createFromPath(iconPath);
|
||||
const icon = nativeImage.createFromPath(getAppIconPath());
|
||||
return icon.isEmpty() ? undefined : icon;
|
||||
}
|
||||
|
||||
function applyRuntimeAppIcon(): void {
|
||||
if (process.platform !== 'darwin' || !app.dock) return;
|
||||
const icon = nativeImage.createFromPath(join(getIconsDir(), 'icon.png'));
|
||||
if (!icon.isEmpty()) {
|
||||
app.dock.setIcon(icon);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the main application window
|
||||
*/
|
||||
@@ -162,6 +173,12 @@ function createWindow(): BrowserWindow {
|
||||
const isWindows = process.platform === 'win32';
|
||||
const useCustomTitleBar = isWindows;
|
||||
const shouldSkipSetupForE2E = process.env.CLAWX_E2E_SKIP_SETUP === '1';
|
||||
const e2eRendererQuery = isE2EMode
|
||||
? {
|
||||
e2e: '1',
|
||||
...(shouldSkipSetupForE2E ? { e2eSkipSetup: '1' } : {}),
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const win = new BrowserWindow({
|
||||
width: 1280,
|
||||
@@ -180,6 +197,7 @@ function createWindow(): BrowserWindow {
|
||||
trafficLightPosition: isMac ? { x: 16, y: 16 } : undefined,
|
||||
frame: isMac || !useCustomTitleBar,
|
||||
show: false,
|
||||
title: PRODUCT_NAME,
|
||||
});
|
||||
|
||||
// Handle external links — only allow safe protocols to prevent arbitrary
|
||||
@@ -201,18 +219,18 @@ function createWindow(): BrowserWindow {
|
||||
// Load the app
|
||||
if (process.env.VITE_DEV_SERVER_URL) {
|
||||
const rendererUrl = new URL(process.env.VITE_DEV_SERVER_URL);
|
||||
if (shouldSkipSetupForE2E) {
|
||||
rendererUrl.searchParams.set('e2eSkipSetup', '1');
|
||||
if (e2eRendererQuery) {
|
||||
Object.entries(e2eRendererQuery).forEach(([key, value]) => {
|
||||
rendererUrl.searchParams.set(key, value);
|
||||
});
|
||||
}
|
||||
win.loadURL(rendererUrl.toString());
|
||||
if (!isE2EMode) {
|
||||
if (!isE2EMode && process.env.YINIAN_OPEN_DEVTOOLS === '1') {
|
||||
win.webContents.openDevTools();
|
||||
}
|
||||
} else {
|
||||
win.loadFile(join(__dirname, '../../dist/index.html'), {
|
||||
query: shouldSkipSetupForE2E
|
||||
? { e2eSkipSetup: '1' }
|
||||
: undefined,
|
||||
query: e2eRendererQuery,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -281,7 +299,7 @@ function createMainWindow(): BrowserWindow {
|
||||
async function initialize(): Promise<void> {
|
||||
// Initialize logger first
|
||||
logger.init();
|
||||
logger.info('=== ClawX Application Starting ===');
|
||||
logger.info('=== 智念助手 Application Starting ===');
|
||||
logger.debug(
|
||||
`Runtime: platform=${process.platform}/${process.arch}, electron=${process.versions.electron}, node=${process.versions.node}, packaged=${app.isPackaged}, pid=${process.pid}, ppid=${process.ppid}`
|
||||
);
|
||||
@@ -469,9 +487,12 @@ async function initialize(): Promise<void> {
|
||||
hostEventBus.emit('channel:whatsapp-error', error);
|
||||
});
|
||||
|
||||
// Start Gateway automatically (this seeds missing bootstrap files with full templates)
|
||||
// YINIAN: do not start Gateway before a user has logged in and a workspace
|
||||
// context has been loaded. Legacy ClawX auto-start can be restored for
|
||||
// debugging with CLAWX_LEGACY_AUTOSTART=1.
|
||||
const gatewayAutoStart = await getSetting('gatewayAutoStart');
|
||||
if (!isE2EMode && gatewayAutoStart) {
|
||||
const legacyAutoStart = process.env.CLAWX_LEGACY_AUTOSTART === '1';
|
||||
if (!isE2EMode && gatewayAutoStart && legacyAutoStart) {
|
||||
try {
|
||||
await syncAllProviderAuthToRuntime();
|
||||
logger.debug('Auto-starting Gateway...');
|
||||
@@ -483,6 +504,8 @@ async function initialize(): Promise<void> {
|
||||
}
|
||||
} else if (isE2EMode) {
|
||||
logger.info('Gateway auto-start skipped in E2E mode');
|
||||
} else if (!legacyAutoStart) {
|
||||
logger.info('Gateway auto-start deferred until YINIAN login');
|
||||
} else {
|
||||
logger.info('Gateway auto-start disabled in settings');
|
||||
}
|
||||
@@ -543,7 +566,7 @@ if (gotTheLock) {
|
||||
|
||||
// When a second instance is launched, focus the existing window instead.
|
||||
app.on('second-instance', () => {
|
||||
logger.info('Second ClawX instance detected; redirecting to the existing window');
|
||||
logger.info('Second 智念助手 instance detected; redirecting to the existing window');
|
||||
|
||||
const focusRequest = requestSecondInstanceFocus(
|
||||
mainWindowFocusState,
|
||||
@@ -560,6 +583,7 @@ if (gotTheLock) {
|
||||
|
||||
// Application lifecycle
|
||||
app.whenReady().then(() => {
|
||||
applyRuntimeAppIcon();
|
||||
void initialize().catch((error) => {
|
||||
logger.error('Application initialization failed:', error);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user