feat: add first-run runtime initialization

This commit is contained in:
inman
2026-04-29 17:24:37 +08:00
parent 3b252250cd
commit cddaf37016
14 changed files with 432 additions and 80 deletions

View File

@@ -44,6 +44,7 @@ import {
import { updateSkillConfig, getSkillConfig, getAllSkillConfigs } from '../utils/skill-config';
import { whatsAppLoginManager } from '../utils/whatsapp-login';
import { getProviderConfig } from '../utils/provider-registry';
import { getYinianInitializationStatus, initializeYinianRuntime } from '../utils/yinian-initializer';
import { deviceOAuthManager, OAuthProviderType } from '../utils/device-oauth';
import { browserOAuthManager, type BrowserOAuthProviderType } from '../utils/browser-oauth';
import { applyProxySettings } from './proxy';
@@ -98,6 +99,9 @@ export function registerIpcHandlers(
// OpenClaw handlers
registerOpenClawHandlers(gatewayManager);
// YINIAN first-run initialization
registerYinianSetupHandlers();
// Provider handlers
registerProviderHandlers(gatewayManager);
@@ -144,6 +148,11 @@ export function registerIpcHandlers(
registerFileHandlers();
}
function registerYinianSetupHandlers(): void {
ipcMain.handle('yinian:setup:status', async () => getYinianInitializationStatus());
ipcMain.handle('yinian:setup:initialize', async () => initializeYinianRuntime());
}
function registerUnifiedRequestHandlers(gatewayManager: GatewayManager): void {
const providerService = getProviderService();
const handleProxySettingsChange = async () => {

View File

@@ -12,11 +12,12 @@ import { logger } from '../utils/logger';
import { EventEmitter } from 'events';
import { setQuitting } from './app-state';
/** Base CDN URL (without trailing channel path) */
const OSS_BASE_URL = 'https://oss.intelli-spectrum.com';
/** Update feed URL. Disabled unless explicitly configured for an environment. */
const UPDATE_FEED_BASE_URL = process.env.YINIAN_UPDATE_FEED_URL?.trim() || '';
const UPDATES_DISABLED_MESSAGE = '内测阶段暂未启用在线更新,请使用服务端下发的新版安装包。';
export interface UpdateStatus {
status: 'idle' | 'checking' | 'available' | 'not-available' | 'downloading' | 'downloaded' | 'error';
status: 'idle' | 'disabled' | 'checking' | 'available' | 'not-available' | 'downloading' | 'downloaded' | 'error';
info?: UpdateInfo;
progress?: ProgressInfo;
error?: string;
@@ -43,7 +44,9 @@ function detectChannel(version: string): string {
export class AppUpdater extends EventEmitter {
private mainWindow: BrowserWindow | null = null;
private status: UpdateStatus = { status: 'idle' };
private status: UpdateStatus = UPDATE_FEED_BASE_URL
? { status: 'idle' }
: { status: 'disabled', error: UPDATES_DISABLED_MESSAGE };
private autoInstallTimer: NodeJS.Timeout | null = null;
private autoInstallCountdown = 0;
@@ -69,13 +72,16 @@ export class AppUpdater extends EventEmitter {
debug: (msg: string) => logger.debug('[Updater]', msg),
};
// Override feed URL for prerelease channels so that
// alpha -> /alpha/alpha-mac.yml, beta -> /beta/beta-mac.yml, etc.
const version = app.getVersion();
const channel = detectChannel(version);
const feedUrl = `${OSS_BASE_URL}/${channel}`;
const feedUrl = UPDATE_FEED_BASE_URL ? `${UPDATE_FEED_BASE_URL.replace(/\/+$/, '')}/${channel}` : '';
logger.info(`[Updater] Version: ${version}, channel: ${channel}, feedUrl: ${feedUrl}`);
logger.info(`[Updater] Version: ${version}, channel: ${channel}, feedUrl: ${feedUrl || 'disabled'}`);
if (!UPDATE_FEED_BASE_URL) {
autoUpdater.autoDownload = false;
autoUpdater.autoInstallOnAppQuit = false;
return;
}
// Set channel so electron-updater requests the correct yml filename.
// e.g. channel "alpha" → requests alpha-mac.yml, channel "latest" → requests latest-mac.yml
@@ -174,6 +180,11 @@ export class AppUpdater extends EventEmitter {
* final status so the UI never gets stuck in 'checking'.
*/
async checkForUpdates(): Promise<UpdateInfo | null> {
if (!UPDATE_FEED_BASE_URL) {
this.updateStatus({ status: 'disabled', error: UPDATES_DISABLED_MESSAGE });
return null;
}
try {
const result = await autoUpdater.checkForUpdates();
@@ -205,6 +216,11 @@ export class AppUpdater extends EventEmitter {
* Download available update
*/
async downloadUpdate(): Promise<void> {
if (!UPDATE_FEED_BASE_URL) {
this.updateStatus({ status: 'disabled', error: UPDATES_DISABLED_MESSAGE });
throw new Error(UPDATES_DISABLED_MESSAGE);
}
try {
await autoUpdater.downloadUpdate();
} catch (error) {
@@ -225,6 +241,11 @@ export class AppUpdater extends EventEmitter {
* the window cleanly while ShipIt runs independently to replace the app.
*/
quitAndInstall(): void {
if (!UPDATE_FEED_BASE_URL) {
this.updateStatus({ status: 'disabled', error: UPDATES_DISABLED_MESSAGE });
return;
}
logger.info('[Updater] quitAndInstall called');
setQuitting();
autoUpdater.quitAndInstall();
@@ -273,7 +294,7 @@ export class AppUpdater extends EventEmitter {
* Set auto-download preference
*/
setAutoDownload(enable: boolean): void {
autoUpdater.autoDownload = enable;
autoUpdater.autoDownload = UPDATE_FEED_BASE_URL ? enable : false;
}
/**