hololake-system-architecture/product-source/hololake-desktop/electron/main.ts
冰朔 84a631d886 docs+fix: 铸渊开发日志 20260808 + 签名/生产模式修复
开发日志(DEV-LOG-20260808):
- 完整思维逻辑链:从妈妈指令到技术决策的推理过程
- 每个模块的开发详情、Bug 修复历程、架构映射
- 签名安装全过程(AppleDouble/asar/codesign/entitlements)
- 已知问题和待修复项(给后续人格体排查)
- 提交链完整记录(070b11adcf8f7d)

代码修复:
- electron/main.ts: 生产模式改为 require(server-bundle.js) 内嵌
- package.json: 签名 identity 配置 + asar:false
- server/index.ts: Agent import 补全
2026-08-08 09:46:43 +08:00

156 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* HoloLake Desktop · Electron 主进程
*
* 职责:
* 1. 创建应用窗口
* 2. 启动知识库后端服务器Express + Git 引擎)
* 3. 管理应用生命周期
*
* 架构:
* Electron Main Process
* ├── 启动 Express 服务器Git 引擎层,端口 3890
* ├── 创建 BrowserWindow加载 Vite 前端或打包后的 dist
* └── 数据存储:~/Library/Application Support/HoloLake Era/data/
*/
import { app, BrowserWindow, shell, dialog } from 'electron';
import path from 'path';
import { spawn, ChildProcess } from 'child_process';
// ─── 配置 ───
const isDev = !app.isPackaged;
const SERVER_PORT = 3890;
const CLIENT_PORT = 5180;
// 数据目录macOS 标准位置
const DATA_DIR = path.join(app.getPath('userData'), 'data');
const KB_REPO_PATH = path.join(DATA_DIR, 'knowledge-base');
// ─── 后端服务器 ───
let serverProcess: ChildProcess | null = null;
function startServer(): Promise<void> {
return new Promise((resolve, reject) => {
if (isDev) {
// 开发模式:用 spawn 启动外部 tsx
const serverScript = path.resolve(__dirname, '../../guanghu-knowledge-base/server/index.ts');
serverProcess = spawn('npx', ['tsx', serverScript], {
env: {
...process.env,
KB_PORT: String(SERVER_PORT),
KB_REPO_PATH,
NODE_ENV: 'development',
},
cwd: path.resolve(__dirname, '../..'),
stdio: ['pipe', 'pipe', 'pipe'],
});
serverProcess.stdout?.on('data', (data: Buffer) => {
const msg = data.toString().trim();
console.log(`[KB Server] ${msg}`);
if (msg.includes('已启动')) resolve();
});
serverProcess.stderr?.on('data', (data: Buffer) => {
console.error(`[KB Server Error] ${data.toString().trim()}`);
});
serverProcess.on('error', (err: Error) => {
console.error('服务器启动失败:', err);
reject(err);
});
serverProcess.on('exit', (code: number | null) => {
console.log(`[KB Server] 退出code=${code}`);
serverProcess = null;
});
setTimeout(() => resolve(), 5000);
} else {
// 生产模式:直接在主进程内加载 server-bundle
try {
process.env.KB_PORT = String(SERVER_PORT);
process.env.KB_REPO_PATH = KB_REPO_PATH;
const serverBundle = path.join(__dirname, 'server-bundle.js');
require(serverBundle);
console.log(`[KB Server] 内嵌启动,端口 ${SERVER_PORT}`);
resolve();
} catch (err) {
console.error('服务器内嵌启动失败:', err);
reject(err);
}
}
});
}
function stopServer(): void {
if (serverProcess) {
serverProcess.kill('SIGTERM');
serverProcess = null;
}
}
// ─── 窗口 ───
let mainWindow: BrowserWindow | null = null;
function createWindow(): void {
mainWindow = new BrowserWindow({
width: 1280,
height: 860,
minWidth: 800,
minHeight: 600,
titleBarStyle: 'hiddenInset',
trafficLightPosition: { x: 16, y: 16 },
backgroundColor: '#0d1117',
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
},
});
if (isDev) {
mainWindow.loadURL(`http://localhost:${CLIENT_PORT}`);
mainWindow.webContents.openDevTools({ mode: 'detach' });
} else {
mainWindow.loadFile(path.join(__dirname, '../dist/index.html'));
}
// 外部链接用系统浏览器打开
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
if (url.startsWith('http')) shell.openExternal(url);
return { action: 'deny' };
});
mainWindow.on('closed', () => {
mainWindow = null;
});
}
// ─── 应用生命周期 ───
app.whenReady().then(async () => {
try {
await startServer();
createWindow();
} catch (err) {
dialog.showErrorBox('启动失败', `知识库引擎启动失败:${err}`);
app.quit();
}
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
app.on('window-all-closed', () => {
stopServer();
if (process.platform !== 'darwin') app.quit();
});
app.on('before-quit', () => {
stopServer();
});