mirror of
https://github.com/EKKOLearnAI/hermes-web-ui.git
synced 2026-07-21 07:50:16 +00:00
feat: add desktop MCP command shim
This commit is contained in:
@@ -6,6 +6,7 @@ import { join } from 'node:path'
|
||||
|
||||
const DEFAULT_PORT = process.env.HERMES_WEB_UI_PORT || process.env.PORT || '8648'
|
||||
const DEFAULT_BASE_URL = `http://127.0.0.1:${DEFAULT_PORT}`
|
||||
const SERVER_NAME = process.env.HERMES_MCP_SERVER_NAME || 'hermes-web-ui-mcp'
|
||||
|
||||
function appHome() {
|
||||
return process.env.HERMES_WEB_UI_HOME ||
|
||||
@@ -274,7 +275,7 @@ async function handle(message) {
|
||||
result: {
|
||||
protocolVersion: message.params?.protocolVersion || '2024-11-05',
|
||||
capabilities: { tools: {} },
|
||||
serverInfo: { name: 'hermes-lan-peer-mcp', version: '0.1.0' },
|
||||
serverInfo: { name: SERVER_NAME, version: '0.1.0' },
|
||||
},
|
||||
}
|
||||
case 'tools/list':
|
||||
+1
-1
@@ -31,7 +31,7 @@
|
||||
],
|
||||
"bin": {
|
||||
"hermes-web-ui": "./bin/hermes-web-ui.mjs",
|
||||
"hermes-lan-peer-mcp": "./bin/hermes-lan-peer-mcp.mjs"
|
||||
"hermes-web-ui-mcp": "./bin/hermes-web-ui-mcp.mjs"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "vite --host --port 8648",
|
||||
|
||||
@@ -38,6 +38,7 @@ extraResources:
|
||||
to: "webui"
|
||||
filter:
|
||||
- "package.json"
|
||||
- "bin/**"
|
||||
- "dist/**"
|
||||
- "node_modules/**"
|
||||
# Drop other-platform node-pty prebuilds (saves ~45MB)
|
||||
|
||||
@@ -15,6 +15,7 @@ import { HERMES_CLI_ARG } from './cli-constants'
|
||||
const execFileAsync = promisify(execFile)
|
||||
|
||||
const SHIM_MARKER = 'HERMES_STUDIO_CLI_SHIM'
|
||||
const MCP_SHIM_MARKER = 'HERMES_STUDIO_MCP_SHIM'
|
||||
const PATH_MARKER_START = '# >>> Hermes Studio CLI shim >>>'
|
||||
const PATH_MARKER_END = '# <<< Hermes Studio CLI shim <<<'
|
||||
|
||||
@@ -34,6 +35,11 @@ interface CliShimInstallOptions {
|
||||
platform?: NodeJS.Platform
|
||||
}
|
||||
|
||||
interface McpShimInstallOptions extends CliShimInstallOptions {
|
||||
nodePath?: string
|
||||
scriptPath?: string
|
||||
}
|
||||
|
||||
function platformDelimiter(platform: NodeJS.Platform): string {
|
||||
return platform === 'win32' ? ';' : delimiter
|
||||
}
|
||||
@@ -62,6 +68,10 @@ export function shimPathForPlatform(binDir: string, platform: NodeJS.Platform =
|
||||
return join(binDir, platform === 'win32' ? 'hermes-studio.cmd' : 'hermes-studio')
|
||||
}
|
||||
|
||||
export function mcpShimPathForPlatform(binDir: string, platform: NodeJS.Platform = process.platform): string {
|
||||
return join(binDir, platform === 'win32' ? 'hermes-studio-mcp.cmd' : 'hermes-studio-mcp')
|
||||
}
|
||||
|
||||
function shellQuote(value: string): string {
|
||||
return `'${value.replace(/'/g, `'\\''`)}'`
|
||||
}
|
||||
@@ -112,15 +122,62 @@ export function createShimContent(
|
||||
].join('\n')
|
||||
}
|
||||
|
||||
function isManagedShim(content: string): boolean {
|
||||
return content.includes(SHIM_MARKER)
|
||||
export function createMcpShimContent(
|
||||
nodePath: string,
|
||||
scriptPath: string,
|
||||
platform: NodeJS.Platform = process.platform,
|
||||
): string {
|
||||
if (platform === 'win32') {
|
||||
return [
|
||||
'@echo off',
|
||||
`rem ${MCP_SHIM_MARKER}`,
|
||||
`set "NODE=${nodePath}"`,
|
||||
`set "SCRIPT=${scriptPath}"`,
|
||||
'if not exist "%NODE%" (',
|
||||
' echo Hermes Studio Node runtime not found at "%NODE%" 1>&2',
|
||||
' echo Open Hermes Studio once to finish runtime setup, then retry hermes-studio-mcp. 1>&2',
|
||||
' exit /b 127',
|
||||
')',
|
||||
'if not exist "%SCRIPT%" (',
|
||||
' echo Hermes Studio MCP script not found at "%SCRIPT%" 1>&2',
|
||||
' exit /b 127',
|
||||
')',
|
||||
'set "HERMES_MCP_SERVER_NAME=hermes-studio-mcp"',
|
||||
'"%NODE%" "%SCRIPT%" %*',
|
||||
'exit /b %ERRORLEVEL%',
|
||||
'',
|
||||
].join('\r\n')
|
||||
}
|
||||
|
||||
return [
|
||||
'#!/bin/sh',
|
||||
`# ${MCP_SHIM_MARKER}`,
|
||||
`NODE=${shellQuote(nodePath)}`,
|
||||
`SCRIPT=${shellQuote(scriptPath)}`,
|
||||
'if [ ! -x "$NODE" ]; then',
|
||||
' echo "Hermes Studio Node runtime not found at $NODE" >&2',
|
||||
' echo "Open Hermes Studio once to finish runtime setup, then retry hermes-studio-mcp." >&2',
|
||||
' exit 127',
|
||||
'fi',
|
||||
'if [ ! -f "$SCRIPT" ]; then',
|
||||
' echo "Hermes Studio MCP script not found at $SCRIPT" >&2',
|
||||
' exit 127',
|
||||
'fi',
|
||||
'export HERMES_MCP_SERVER_NAME=hermes-studio-mcp',
|
||||
'exec "$NODE" "$SCRIPT" "$@"',
|
||||
'',
|
||||
].join('\n')
|
||||
}
|
||||
|
||||
function writeShim(shimPath: string, content: string, platform: NodeJS.Platform): ShimInstallStatus {
|
||||
function isManagedShim(content: string, marker: string): boolean {
|
||||
return content.includes(marker)
|
||||
}
|
||||
|
||||
function writeShim(shimPath: string, content: string, platform: NodeJS.Platform, marker = SHIM_MARKER): ShimInstallStatus {
|
||||
if (existsSync(shimPath)) {
|
||||
const existing = readFileSync(shimPath, 'utf-8')
|
||||
if (existing === content) return 'unchanged'
|
||||
if (!isManagedShim(existing)) return 'skipped'
|
||||
if (!isManagedShim(existing, marker)) return 'skipped'
|
||||
writeFileSync(shimPath, content, 'utf-8')
|
||||
if (platform !== 'win32') chmodSync(shimPath, 0o755)
|
||||
return 'updated'
|
||||
@@ -245,3 +302,27 @@ export async function installHermesStudioCliShim(options: CliShimInstallOptions
|
||||
reason: status === 'skipped' ? 'existing hermes-studio shim is not managed by Hermes Studio' : undefined,
|
||||
}
|
||||
}
|
||||
|
||||
export async function installHermesStudioMcpShim(options: McpShimInstallOptions = {}): Promise<CliShimInstallResult> {
|
||||
const platform = options.platform || process.platform
|
||||
const env = options.env || process.env
|
||||
const homeDir = options.homeDir || homedir()
|
||||
const binDir = resolve(homeDir, 'bin')
|
||||
const shimPath = mcpShimPathForPlatform(binDir, platform)
|
||||
const nodePath = options.nodePath || process.execPath
|
||||
const scriptPath = options.scriptPath || resolve(process.cwd(), 'bin', 'hermes-web-ui-mcp.mjs')
|
||||
|
||||
mkdirSync(binDir, { recursive: true })
|
||||
const status = writeShim(shimPath, createMcpShimContent(nodePath, scriptPath, platform), platform, MCP_SHIM_MARKER)
|
||||
const pathUpdated = await ensureUserBinOnPath(homeDir, binDir, platform, env).catch((err) => {
|
||||
console.warn(`[cli-shim] failed to update PATH: ${err instanceof Error ? err.message : String(err)}`)
|
||||
return false
|
||||
})
|
||||
|
||||
return {
|
||||
shimPath,
|
||||
status,
|
||||
pathUpdated,
|
||||
reason: status === 'skipped' ? 'existing hermes-studio-mcp shim is not managed by Hermes Studio' : undefined,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { app, BrowserWindow, Menu, Tray, shell, ipcMain, nativeImage } from 'electron'
|
||||
import { join } from 'node:path'
|
||||
import { startWebUiServer, stopWebUiServer, getToken } from './webui-server'
|
||||
import { desktopIcon, desktopTrayTemplateIcon, desktopWindowsTrayIcon, hermesBinExists, hermesBin } from './paths'
|
||||
import { bundledNode, desktopIcon, desktopTrayTemplateIcon, desktopWindowsTrayIcon, hermesBinExists, hermesBin, webuiDir } from './paths'
|
||||
import { checkForDesktopUpdates, initAutoUpdater } from './updater'
|
||||
import { t } from './desktop-i18n'
|
||||
import { installHermesStudioCliShim } from './cli-shim'
|
||||
import { installHermesStudioCliShim, installHermesStudioMcpShim } from './cli-shim'
|
||||
import { parseHermesCliArgs, runBundledHermesCli } from './hermes-cli'
|
||||
import {
|
||||
cachedRuntimeNeedsPackagedReleaseUpdate,
|
||||
@@ -451,6 +451,16 @@ function runDesktopApp() {
|
||||
}).catch(err => {
|
||||
console.warn(`[cli-shim] failed to install hermes-studio command: ${err instanceof Error ? err.message : String(err)}`)
|
||||
})
|
||||
installHermesStudioMcpShim({
|
||||
nodePath: bundledNode(),
|
||||
scriptPath: join(webuiDir(), 'bin', 'hermes-web-ui-mcp.mjs'),
|
||||
}).then(result => {
|
||||
if (result.status === 'skipped') {
|
||||
console.warn(`[cli-shim] ${result.reason}: ${result.shimPath}`)
|
||||
}
|
||||
}).catch(err => {
|
||||
console.warn(`[cli-shim] failed to install hermes-studio-mcp command: ${err instanceof Error ? err.message : String(err)}`)
|
||||
})
|
||||
}
|
||||
createTray()
|
||||
createWindow()
|
||||
|
||||
Reference in New Issue
Block a user