48 lines
2.2 KiB
TypeScript
48 lines
2.2 KiB
TypeScript
import { delimiter, join, resolve } from 'node:path'
|
|
import { homedir, tmpdir } from 'node:os'
|
|
import { createHash } from 'node:crypto'
|
|
|
|
export function desktopEnvironment(root: string, stateHome: string, port: number, env: NodeJS.ProcessEnv = process.env): NodeJS.ProcessEnv {
|
|
const home = homedir()
|
|
return {
|
|
...env,
|
|
NODE_ENV: 'production',
|
|
HERMES_DESKTOP: 'true',
|
|
BIND_HOST: '127.0.0.1',
|
|
PORT: String(port),
|
|
HERMES_WEB_UI_HOME: stateHome,
|
|
HERMES_WEBUI_STATE_DIR: stateHome,
|
|
HERMES_WEB_UI_MCP_BIN: join(root, 'bin/hermes-ui-mcp.mjs'),
|
|
HERMES_WEB_UI_STOP_GATEWAYS_ON_SHUTDOWN: '1',
|
|
HERMES_AGENT_BRIDGE_STOP_ON_SHUTDOWN: '1',
|
|
HERMES_LAN_DISCOVERY_ENABLED: 'false',
|
|
HERMES_WEB_UI_DISABLE_UPDATE_CHECK: 'true',
|
|
...(process.platform === 'win32' ? {} : {
|
|
HERMES_AGENT_BRIDGE_ENDPOINT: env.HERMES_AGENT_BRIDGE_ENDPOINT || `ipc://${join(tmpdir(), `hermes-desktop-${createHash('sha256').update(stateHome).digest('hex').slice(0, 16)}.sock`)}`,
|
|
}),
|
|
PATH: [env.PATH, join(home, '.local/bin'), join(home, '.npm-global/bin'), '/opt/homebrew/bin', '/usr/local/bin'].filter(Boolean).join(delimiter),
|
|
}
|
|
}
|
|
|
|
export function desktopStateHome(userData: string, env: NodeJS.ProcessEnv = process.env): string {
|
|
return resolve(env.HERMES_WEB_UI_HOME?.trim() || env.HERMES_WEBUI_STATE_DIR?.trim() || join(userData, 'state'))
|
|
}
|
|
|
|
export function isInternalUrl(value: string, origin: string): boolean {
|
|
try { return new URL(value).origin === origin } catch { return false }
|
|
}
|
|
|
|
export function isExternalUrl(value: string): boolean {
|
|
try { return ['https:', 'http:', 'mailto:'].includes(new URL(value).protocol) } catch { return false }
|
|
}
|
|
|
|
export function storedPort(value: unknown): number {
|
|
return typeof value === 'number' && Number.isInteger(value) && value >= 1024 && value <= 65535 ? value : 0
|
|
}
|
|
|
|
export function isDesktopPermissionAllowed(permission: string, requestingOrigin: string, origin: string, mediaTypes: string[] = []): boolean {
|
|
if (!isInternalUrl(requestingOrigin, origin)) return false
|
|
if (permission === 'notifications' || permission === 'clipboard-sanitized-write') return true
|
|
return permission === 'media' && mediaTypes.length > 0 && mediaTypes.every(type => type === 'audio')
|
|
}
|