154 lines
5.0 KiB
TypeScript
154 lines
5.0 KiB
TypeScript
import log from "electron-log";
|
||
import { app, BrowserWindow } from "electron";
|
||
import { getDb, readSetting } from "../db";
|
||
import { startComputerServer } from "../services/computerServer";
|
||
import {
|
||
mcpProxyManager,
|
||
DEFAULT_MCP_PROXY_CONFIG,
|
||
pruneDisabledDefaultMcpServers,
|
||
} from "../services/packages/mcp";
|
||
import { getConfiguredPorts } from "../services/startupPorts";
|
||
import { DEPS_SYNC_TIMEOUT } from "@shared/constants";
|
||
import {
|
||
startSandboxService,
|
||
stopSandboxService,
|
||
} from "../services/sandbox/serviceBootstrap";
|
||
|
||
/** 依赖同步是否正在进行 */
|
||
let _depsSyncInProgress = false;
|
||
export function isDepsSyncInProgress(): boolean {
|
||
return _depsSyncInProgress;
|
||
}
|
||
|
||
export async function runStartupTasks(): Promise<void> {
|
||
// 从 SQLite 恢复镜像配置
|
||
const {
|
||
setMirrorConfig,
|
||
getInitDepsState,
|
||
syncInitDependencies,
|
||
getSetupRequiredDependencies,
|
||
} = await import("../services/system/dependencies");
|
||
const mirrorConfig = readSetting("mirror_config");
|
||
if (mirrorConfig) {
|
||
try {
|
||
setMirrorConfig(mirrorConfig);
|
||
} catch (e) {
|
||
log.warn("[Mirror] Failed to apply mirror config:", e);
|
||
}
|
||
}
|
||
|
||
// 尽早启动 Computer HTTP Server(对齐 rcoder /computer/* API),端口来自聚合配置
|
||
{
|
||
const { agent: agentPort } = getConfiguredPorts();
|
||
startComputerServer(agentPort).then((r) => {
|
||
if (r.success)
|
||
log.info(`[Init] Computer HTTP server listening on port ${agentPort}`);
|
||
else log.warn(`[Init] Computer HTTP server failed: ${r.error}`);
|
||
});
|
||
}
|
||
|
||
// 初始化 MCP Proxy 配置(从数据库加载)
|
||
try {
|
||
const db = getDb();
|
||
const savedConfig = db
|
||
?.prepare("SELECT value FROM settings WHERE key = ?")
|
||
.get("mcp_proxy_config") as { value: string } | undefined;
|
||
if (savedConfig) {
|
||
try {
|
||
const parsed = JSON.parse(savedConfig.value);
|
||
// 合并可用的默认服务器;旧版本持久化的 chrome-devtools npx 默认项
|
||
// 在本地未安装且未显式启用时会被过滤,避免启动时阻塞 60s。
|
||
const merged = {
|
||
...parsed,
|
||
mcpServers: pruneDisabledDefaultMcpServers({
|
||
...DEFAULT_MCP_PROXY_CONFIG.mcpServers,
|
||
...(parsed.mcpServers || {}),
|
||
}),
|
||
};
|
||
mcpProxyManager.setConfig(merged);
|
||
} catch (e) {
|
||
log.warn("[McpProxy] Init config parse failed:", e);
|
||
}
|
||
}
|
||
log.info("[McpProxy] Config loaded");
|
||
} catch (e) {
|
||
log.warn("[McpProxy] Init config failed:", e);
|
||
}
|
||
|
||
// 初始化沙箱服务(后台启动,不阻塞主流程)
|
||
setImmediate(async () => {
|
||
try {
|
||
await startSandboxService();
|
||
log.info("[Sandbox] Sandbox service started");
|
||
} catch (e) {
|
||
log.warn("[Sandbox] Sandbox service start failed (non-fatal):", e);
|
||
// 沙箱服务失败不阻塞应用,只是功能降级
|
||
}
|
||
});
|
||
|
||
// 客户端升级后:若 appVersion 或 installVersion 变化,后台同步初始化依赖到写死版本
|
||
// 同步检查是否需要 dep sync,提前设置标志,避免 renderer 在 setImmediate 之前
|
||
// 读到 syncInProgress=false 而过早启动服务(竞态条件)
|
||
const state = getInitDepsState();
|
||
const currentVersion = app.getVersion();
|
||
const versionChanged = !state || state.appVersion !== currentVersion;
|
||
let packagesChanged = false;
|
||
if (state?.packages) {
|
||
for (const dep of getSetupRequiredDependencies()) {
|
||
if (!dep.installVersion) continue;
|
||
if (state.packages[dep.name] !== dep.installVersion) {
|
||
packagesChanged = true;
|
||
break;
|
||
}
|
||
}
|
||
} else {
|
||
packagesChanged = true;
|
||
}
|
||
const needsSync = versionChanged || packagesChanged;
|
||
if (needsSync) {
|
||
_depsSyncInProgress = true;
|
||
}
|
||
|
||
setImmediate(async () => {
|
||
if (!needsSync) return;
|
||
try {
|
||
try {
|
||
let syncTimer: ReturnType<typeof setTimeout>;
|
||
const { updated } = await Promise.race([
|
||
syncInitDependencies(),
|
||
new Promise<never>((_, reject) => {
|
||
syncTimer = setTimeout(
|
||
() =>
|
||
reject(
|
||
new Error(
|
||
`Dependency sync timeout after ${DEPS_SYNC_TIMEOUT}ms`,
|
||
),
|
||
),
|
||
DEPS_SYNC_TIMEOUT,
|
||
);
|
||
}),
|
||
]).finally(() => clearTimeout(syncTimer!));
|
||
if (updated.length > 0)
|
||
log.info("[Init] Init dependencies synced:", updated);
|
||
} finally {
|
||
_depsSyncInProgress = false;
|
||
// 通知所有渲染进程依赖同步完成,重新检测
|
||
for (const win of BrowserWindow.getAllWindows()) {
|
||
if (!win.isDestroyed()) {
|
||
win.webContents.send("deps:syncCompleted");
|
||
}
|
||
}
|
||
}
|
||
} catch (e) {
|
||
_depsSyncInProgress = false;
|
||
// 同步失败也通知渲染进程重新检测实际状态
|
||
for (const win of BrowserWindow.getAllWindows()) {
|
||
if (!win.isDestroyed()) {
|
||
win.webContents.send("deps:syncCompleted");
|
||
}
|
||
}
|
||
log.warn("[Init] Init dependencies sync failed:", e);
|
||
}
|
||
});
|
||
}
|