Files
bilin/src/main/services/update.service.ts
T
bing 8e5045b882 fix(test): stabilize E2E cockpit flow and add LM Studio novel integration test
Harden cockpit dismiss timing and global settings navigation in E2E helpers, tune short-chapter AI prompts for local models, fix electron-updater CJS loading, and add a 10-chapter auto-writing integration test against LM Studio.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-09 14:03:19 +08:00

110 lines
3.1 KiB
TypeScript

import { createRequire } from 'node:module'
import { app, BrowserWindow } from 'electron'
import type { AppUpdater } from 'electron-updater'
import { IPC } from '../../shared/ipc-channels'
const require = createRequire(import.meta.url)
let autoUpdaterRef: AppUpdater | null = null
function getAutoUpdater(): AppUpdater {
if (!autoUpdaterRef) {
const electronUpdater = require('electron-updater') as { autoUpdater: AppUpdater }
autoUpdaterRef = electronUpdater.autoUpdater
}
return autoUpdaterRef
}
export interface UpdateInfoPayload {
version: string
releaseNotes?: string
}
export interface UpdateProgressPayload {
percent: number
transferred: number
total: number
}
export class UpdateService {
private disabled: boolean
private mockVersion: string | null = null
constructor() {
if (process.env.BILIN_MOCK_UPDATE === '1') {
this.disabled = false
this.mockVersion = process.env.BILIN_MOCK_UPDATE_VERSION ?? '9.9.9'
return
}
this.disabled =
process.env.BILIN_E2E === '1' ||
!app.isPackaged ||
process.env.NODE_ENV === 'development'
const autoUpdater = getAutoUpdater()
autoUpdater.autoDownload = false
autoUpdater.autoInstallOnAppQuit = true
autoUpdater.on('update-available', (info) => {
this.broadcast(IPC.UPDATE_AVAILABLE, {
version: info.version,
releaseNotes: typeof info.releaseNotes === 'string' ? info.releaseNotes : undefined
})
})
autoUpdater.on('download-progress', (p) => {
this.broadcast(IPC.UPDATE_PROGRESS, {
percent: p.percent,
transferred: p.transferred,
total: p.total
})
})
autoUpdater.on('update-downloaded', (info) => {
this.broadcast(IPC.UPDATE_DOWNLOADED, { version: info.version })
})
autoUpdater.on('error', (err) => {
this.broadcast(IPC.UPDATE_ERROR, { message: err.message })
})
}
async check(): Promise<UpdateInfoPayload | null> {
if (this.disabled) return null
if (this.mockVersion) {
const payload = { version: this.mockVersion, releaseNotes: 'Mock update' }
this.broadcast(IPC.UPDATE_AVAILABLE, payload)
return payload
}
const result = await getAutoUpdater().checkForUpdates()
const info = result?.updateInfo
if (!info) return null
return {
version: info.version,
releaseNotes: typeof info.releaseNotes === 'string' ? info.releaseNotes : undefined
}
}
async download(): Promise<void> {
if (this.disabled) return
if (this.mockVersion) {
this.broadcast(IPC.UPDATE_PROGRESS, { percent: 50, transferred: 50, total: 100 })
this.broadcast(IPC.UPDATE_DOWNLOADED, { version: this.mockVersion })
return
}
await getAutoUpdater().downloadUpdate()
}
install(): void {
if (this.disabled || this.mockVersion) return
getAutoUpdater().quitAndInstall()
}
private broadcast(channel: string, payload: unknown): void {
try {
for (const win of BrowserWindow.getAllWindows()) {
win.webContents.send(channel, payload)
}
} catch {
/* test env */
}
}
}