Fix desktop runtime cold start handling (#1233)
* fix desktop runtime cold start handling * fix windows desktop python startup env * Revert "fix windows desktop python startup env" This reverts commit 3718ba7586ab1a672c7e599ff1e315dfa76d7cda. * bump desktop release version to 0.6.8
This commit is contained in:
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hermes-web-ui",
|
||||
"version": "0.6.7",
|
||||
"version": "0.6.8",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hermes-web-ui",
|
||||
"version": "0.6.7",
|
||||
"version": "0.6.8",
|
||||
"description": "Self-hosted AI chat dashboard for Hermes Agent — multi-model web UI with multi-platform integration",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "hermes-studio",
|
||||
"version": "0.6.7",
|
||||
"version": "0.6.8",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "hermes-studio",
|
||||
"version": "0.6.7",
|
||||
"version": "0.6.8",
|
||||
"license": "BSL-1.1",
|
||||
"dependencies": {
|
||||
"electron-updater": "^6.3.9"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hermes-studio",
|
||||
"version": "0.6.7",
|
||||
"version": "0.6.8",
|
||||
"description": "Hermes Studio desktop distribution with bundled Python runtime and hermes-agent",
|
||||
"homepage": "https://ekkolearnai.com",
|
||||
"author": {
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
} from 'node:fs'
|
||||
import { get as httpGet } from 'node:http'
|
||||
import { get as httpsGet } from 'node:https'
|
||||
import { basename, dirname, join } from 'node:path'
|
||||
import { basename, dirname, join, relative } from 'node:path'
|
||||
import { promisify } from 'node:util'
|
||||
import { app } from 'electron'
|
||||
import {
|
||||
@@ -58,6 +58,25 @@ export type RuntimeProgress = {
|
||||
|
||||
type RuntimeProgressHandler = (progress: RuntimeProgress) => void
|
||||
|
||||
function requiredRuntimeFiles(root: string): string[] {
|
||||
const pythonBin = process.platform === 'win32'
|
||||
? join(root, 'python', 'python.exe')
|
||||
: join(root, 'python', 'bin', 'python3')
|
||||
const hermesBin = process.platform === 'win32'
|
||||
? join(root, 'python', 'Scripts', 'hermes.exe')
|
||||
: join(root, 'python', 'bin', 'hermes')
|
||||
const nodeBin = process.platform === 'win32'
|
||||
? join(root, 'node', 'node.exe')
|
||||
: join(root, 'node', 'bin', 'node')
|
||||
const files = [pythonBin, hermesBin, nodeBin, join(root, RUNTIME_MANIFEST_NAME)]
|
||||
if (process.platform === 'win32') files.push(join(root, 'git', 'cmd', 'git.exe'))
|
||||
return files
|
||||
}
|
||||
|
||||
function missingRuntimeFiles(root: string): string[] {
|
||||
return requiredRuntimeFiles(root).filter(file => !existsSync(file))
|
||||
}
|
||||
|
||||
function runtimeReady(): boolean {
|
||||
const gitReady = process.platform !== 'win32' || !!bundledGit()
|
||||
return hermesBinExists() && existsSync(bundledNode()) && gitReady
|
||||
@@ -230,10 +249,9 @@ async function extractRuntimeArchive(archive: string, targetRoot: string): Promi
|
||||
await execFileAsync(process.platform === 'win32' ? 'tar.exe' : 'tar', ['-xzf', archive, '-C', tempRoot], {
|
||||
windowsHide: true,
|
||||
})
|
||||
for (const required of ['python', 'node']) {
|
||||
if (!existsSync(join(tempRoot, required))) {
|
||||
throw new Error(`Runtime archive did not contain ${required}/`)
|
||||
}
|
||||
const missing = missingRuntimeFiles(tempRoot)
|
||||
if (missing.length > 0) {
|
||||
throw new Error(`Runtime archive is missing required files: ${missing.map(file => relative(tempRoot, file)).join(', ')}`)
|
||||
}
|
||||
rmSync(targetRoot, { recursive: true, force: true })
|
||||
mkdirSync(parent, { recursive: true })
|
||||
@@ -265,7 +283,10 @@ export async function ensureDesktopRuntime(onProgress?: RuntimeProgressHandler):
|
||||
const archive = join(dirname(runtimeRoot), `${descriptor.name}.download`)
|
||||
console.log(`[runtime] downloading Hermes runtime ${descriptor.name}`)
|
||||
onProgress?.({ stage: 'download', message: `Downloading ${descriptor.name}...` })
|
||||
let archiveSize = 0
|
||||
try {
|
||||
await downloadFile(descriptor.url, archive, onProgress)
|
||||
archiveSize = statSync(archive).size
|
||||
if (descriptor.sha256) {
|
||||
onProgress?.({ stage: 'verify', message: 'Verifying Hermes runtime...' })
|
||||
const actual = await sha256File(archive)
|
||||
@@ -276,8 +297,9 @@ export async function ensureDesktopRuntime(onProgress?: RuntimeProgressHandler):
|
||||
|
||||
onProgress?.({ stage: 'extract', message: 'Extracting Hermes runtime...' })
|
||||
await extractRuntimeArchive(archive, runtimeRoot)
|
||||
const archiveSize = statSync(archive).size
|
||||
} finally {
|
||||
rmSync(archive, { force: true })
|
||||
}
|
||||
|
||||
const manifestPath = join(runtimeRoot, RUNTIME_MANIFEST_NAME)
|
||||
if (!existsSync(manifestPath)) {
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
} from './paths'
|
||||
|
||||
const DEFAULT_PORT = 8748
|
||||
const DEFAULT_READY_TIMEOUT_MS = 30_000
|
||||
const DEFAULT_READY_TIMEOUT_MS = 120_000
|
||||
const AGENT_BRIDGE_STARTED_MARKER = '[bootstrap] agent bridge started'
|
||||
const AGENT_BRIDGE_FAILED_MARKER = '[bootstrap] agent bridge failed to start'
|
||||
const execFileAsync = promisify(execFile)
|
||||
@@ -405,10 +405,10 @@ export async function startWebUiServer(port = DEFAULT_PORT): Promise<string> {
|
||||
})
|
||||
|
||||
const timeoutMs = readyTimeoutMs()
|
||||
await Promise.all([
|
||||
waitForReady(port, timeoutMs),
|
||||
bridgeStartup.wait(timeoutMs),
|
||||
])
|
||||
void bridgeStartup.wait(timeoutMs).catch(err => {
|
||||
console.warn(`[webui] agent bridge was not ready during startup: ${err instanceof Error ? err.message : String(err)}`)
|
||||
})
|
||||
await waitForReady(port, timeoutMs)
|
||||
return getServerUrl(port)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user