From 293f36ecac94b5823d2de6164141c453c95752f9 Mon Sep 17 00:00:00 2001 From: ekko Date: Thu, 16 Apr 2026 09:54:58 +0800 Subject: [PATCH 1/2] fix: always overwrite api_server config on startup Simplify ensureApiServerConfig to unconditionally write default platforms.api_server values, preventing missing config issues. Bump version to 0.2.7. Co-Authored-By: Claude Opus 4.6 --- package.json | 4 ++-- packages/server/src/index.ts | 12 +----------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 870d8000..754f38a2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hermes-web-ui", - "version": "0.2.6", + "version": "0.2.7", "description": "Web dashboard for Hermes Agent — multi-platform AI chat, session management, scheduled jobs, usage analytics & channel configuration (Telegram, Discord, Slack, WhatsApp)", "repository": { "type": "git", @@ -85,4 +85,4 @@ "vite": "^8.0.4", "vue-tsc": "^3.2.6" } -} +} \ No newline at end of file diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 173b91d0..52788872 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -184,17 +184,7 @@ async function ensureApiServerConfig() { if (!cfg.platforms) cfg.platforms = {} if (!cfg.platforms.api_server) cfg.platforms.api_server = {} - const api = cfg.platforms.api_server - let changed = false - - for (const [k, v] of Object.entries(defaults)) { - if (api[k] != null && api[k] !== v) { - api[k] = v - changed = true - } - } - - if (!changed) return + cfg.platforms.api_server = defaults copyFileSync(configPath, configPath + '.bak') writeFileSync(configPath, yaml.dump(cfg), 'utf-8') From 7eab694fe9e8de83969ec85a9868f4559a46ea33 Mon Sep 17 00:00:00 2001 From: later0day Date: Thu, 16 Apr 2026 23:44:43 +0800 Subject: [PATCH 2/2] Prevent macOS terminal sessions from failing after node-pty installs Node-pty's darwin helper can lose its execute bit in local installs, which leaves the module loadable but causes terminal session creation to fail with posix_spawnp errors. Normalize helper permissions before loading node-pty and remove the rebuild-only hint so startup repairs the real failure mode automatically. Constraint: Must preserve Node 24 and the existing node-pty integration path Rejected: Pipe fallback transport | loses true PTY behavior such as proper terminal semantics Rejected: Tell users to run npm rebuild node-pty | rebuild alone did not restore the helper execute bit here Confidence: high Scope-risk: narrow Reversibility: clean Directive: If a future node-pty release changes helper locations, update the helperCandidates list before touching terminal startup again Tested: npm run build Tested: WebSocket terminal session create + echo command on localhost:8648 under Node 24/macOS Not-tested: Non-macOS helper layouts --- packages/server/src/routes/hermes/terminal.ts | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/packages/server/src/routes/hermes/terminal.ts b/packages/server/src/routes/hermes/terminal.ts index 1b8d2061..c602ed4e 100644 --- a/packages/server/src/routes/hermes/terminal.ts +++ b/packages/server/src/routes/hermes/terminal.ts @@ -1,14 +1,42 @@ import { WebSocketServer } from 'ws' import type { Server as HttpServer } from 'http' -import { existsSync } from 'fs' +import { accessSync, chmodSync, constants as fsConstants, existsSync } from 'fs' +import { dirname, join } from 'path' import { getToken } from '../../services/auth' let pty: any = null + +function ensureNodePtySpawnHelperExecutable() { + if (process.platform !== 'darwin') return + + try { + const nodePtyRoot = dirname(require.resolve('node-pty/package.json')) + const helperCandidates = [ + join(nodePtyRoot, 'build', 'Release', 'spawn-helper'), + join(nodePtyRoot, 'build', 'Debug', 'spawn-helper'), + join(nodePtyRoot, 'prebuilds', `${process.platform}-${process.arch}`, 'spawn-helper'), + ] + + for (const helperPath of helperCandidates) { + if (!existsSync(helperPath)) continue + try { + accessSync(helperPath, fsConstants.X_OK) + } catch { + chmodSync(helperPath, 0o755) + console.log(`[Terminal] Restored execute bit for node-pty helper: ${helperPath}`) + } + } + } catch (err: any) { + console.warn(`[Terminal] Could not normalize node-pty helper permissions: ${err?.message || err}`) + } +} + try { + ensureNodePtySpawnHelperExecutable() // eslint-disable-next-line @typescript-eslint/no-require-imports pty = require('node-pty') -} catch { - console.warn('[Terminal] node-pty failed to load, terminal feature disabled') +} catch (err: any) { + console.warn(`[Terminal] node-pty failed to load, terminal feature disabled (${err?.message || 'unknown error'})`) } // ─── Shell detection ──────────────────────────────────────────── @@ -65,7 +93,7 @@ function createSession(shell: string): PtySession { cwd: process.env.HOME || undefined, }) } catch (err: any) { - throw new Error(`Failed to spawn shell "${shell}": ${err.message}. Run "npm rebuild node-pty" to fix.`) + throw new Error(`Failed to spawn shell "${shell}": ${err.message}`) } const session: PtySession = { @@ -295,5 +323,5 @@ export function setupTerminalWebSocket(httpServer: HttpServer) { console.log(`[Terminal] First session created: ${firstSession.id} (${shellName(defaultShell)}, pid ${firstSession.pid})`) }) - console.log(`[Terminal] WebSocket ready at /terminal (shell: ${defaultShell})`) + console.log(`[Terminal] WebSocket ready at /terminal (shell: ${defaultShell}, transport: node-pty)`) }