diff --git a/README.md b/README.md index fac3f90..5a431e2 100644 --- a/README.md +++ b/README.md @@ -230,22 +230,55 @@ and package installs such as `pip install hermes-agent`. ## Web UI Environment Variables -These variables configure Hermes Web UI itself. Provider API keys and Hermes Agent settings are managed separately through Hermes profiles. +These variables configure Hermes Web UI, its local Hermes runtime integration, and development/preview helpers. Provider API keys and Hermes Agent settings are normally managed through Hermes profiles; environment variables here are process-level overrides. | Variable | Default | Description | | --- | --- | --- | | `PORT` | `8648` | Web UI listen port. | | `BIND_HOST` | `0.0.0.0` | Web UI bind host. Set `::` explicitly for IPv6. | | `HERMES_WEB_UI_HOME` | `~/.hermes-web-ui` | Web UI data home for auth token, credentials, logs, DB, and default uploads. `HERMES_WEBUI_STATE_DIR` is also supported as a compatibility alias. | +| `HERMES_WEBUI_STATE_DIR` | unset | Compatibility alias for `HERMES_WEB_UI_HOME`. | | `UPLOAD_DIR` | `$HERMES_WEB_UI_HOME/upload` | Upload root override. Files are stored below profile-scoped subdirectories. | | `CORS_ORIGINS` | `*` | Koa CORS origin setting. | | `AUTH_TOKEN` | auto-generated | Explicit bearer token. If unset, Web UI creates one under `HERMES_WEB_UI_HOME`. | +| `AUTH_JWT_SECRET` | `AUTH_TOKEN` | JWT signing secret override for username/password sessions. | | `PROFILE` | `default` | Startup/default Hermes profile. Runtime requests use the profile selected by the frontend and authorized for the current account. | | `LOG_LEVEL` | `info` | Server log level. | | `BRIDGE_LOG_LEVEL` | `$LOG_LEVEL` or `info` | Bridge log level. | | `MAX_DOWNLOAD_SIZE` | `200MB` | Maximum file download size. | | `MAX_EDIT_SIZE` | `10MB` | Maximum editable file size. | | `WORKSPACE_BASE` | `/opt/data/workspace` | Base directory for workspace browsing. | +| `HERMES_HOME` | platform default | Hermes data home. Windows uses `%LOCALAPPDATA%\hermes`; macOS/Linux uses `~/.hermes`. | +| `HERMES_BIN` | `hermes` | Custom Hermes CLI binary path. | +| `HERMES_AGENT_ROOT` | auto-discovered | Hermes Agent source checkout containing `run_agent.py`. | +| `HERMES_AGENT_BRIDGE_PYTHON` | auto-discovered | Python interpreter used to launch the agent bridge. | +| `HERMES_AGENT_BRIDGE_UV` | auto-discovered | `uv` executable used to launch the agent bridge when available. | +| `UV` | auto-discovered | Fallback `uv` executable path. | +| `PYTHON` | auto-discovered | Fallback Python executable for the agent bridge. | +| `HERMES_AGENT_BRIDGE_ENDPOINT` | platform default | Agent bridge broker endpoint. Windows defaults to `tcp://127.0.0.1:18765`; macOS/Linux defaults to `ipc:///tmp/hermes-agent-bridge.sock`. | +| `HERMES_AGENT_BRIDGE_TIMEOUT_MS` | `120000` | Timeout for Node requests to the bridge broker. | +| `HERMES_AGENT_BRIDGE_CONNECT_RETRY_MS` | `5000` | Short retry window for connecting to the bridge socket. | +| `HERMES_AGENT_BRIDGE_STARTUP_TIMEOUT_MS` | `120000` | Timeout while waiting for the Python bridge to become ready. | +| `HERMES_AGENT_BRIDGE_AUTO_RESTART` | enabled | Auto-restart the bridge broker after unexpected exit. Set `0`, `false`, `no`, or `off` to disable. | +| `HERMES_AGENT_BRIDGE_RESTART_DELAY_MS` | `1000` | Base delay for bridge auto-restart backoff. | +| `HERMES_AGENT_BRIDGE_PLATFORM` | `cli` | Platform identity passed to Hermes Agent. | +| `HERMES_AGENT_BRIDGE_WORKER_TRANSPORT` | platform default | Profile worker transport. Set `tcp` for loopback TCP or `ipc`/`unix` for Unix domain sockets; defaults to Windows TCP and macOS/Linux IPC. | +| `HERMES_AGENT_BRIDGE_WORKER_PORT_BASE` | `18780` | Base port for TCP worker endpoints. | +| `HERMES_BRIDGE_PROVIDER` | profile/default | Provider override for bridge runs. | +| `HERMES_BRIDGE_TOOLSETS` | profile/default | Toolset override for bridge runs. | +| `HERMES_BRIDGE_MAX_TURNS` | profile/default | Maximum turn override for bridge runs. | +| `HERMES_BRIDGE_SUPPRESS_PLATFORM_HINT` | `cli` | Controls bridge platform hint suppression passed to Hermes Agent. | +| `HERMES_OPENROUTER_APP_REFERER` | `https://ekkolearnai.com` | OpenRouter attribution referer sent by bridge runs. | +| `HERMES_OPENROUTER_APP_TITLE` | `Hermes Web UI` | OpenRouter attribution title sent by bridge runs. | +| `HERMES_OPENROUTER_APP_CATEGORIES` | `cli-agent,personal-agent` | OpenRouter attribution categories sent by bridge runs. | +| `HERMES_WEB_UI_MANAGED_GATEWAY` | platform/runtime dependent | Force managed legacy gateway process handling. Set `1`, `true`, `yes`, or `on` to enable. | +| `HERMES_WEB_UI_STOP_GATEWAYS_ON_SHUTDOWN` | enabled in production | Controls whether Web UI shutdown also stops managed gateway processes. Set `0` or `false` to detach them. | +| `GATEWAY_HOST` | `127.0.0.1` | Default gateway host written into profile config for legacy gateway compatibility. | +| `HERMES_WEB_UI_PREVIEW_REPO` | package repository | GitHub repository used by Version Preview. | +| `HERMES_WEB_UI_PREVIEW_AGENT_BRIDGE_TRANSPORT` | platform default | Version Preview broker transport. Set `tcp` to use loopback TCP for Preview on macOS/Linux; when unset, Preview follows `HERMES_AGENT_BRIDGE_WORKER_TRANSPORT=tcp`. | +| `HERMES_WEB_UI_PREVIEW_AGENT_BRIDGE_ENDPOINT` | isolated preview endpoint | Directly overrides the Version Preview broker endpoint. | +| `HERMES_WEB_UI_BACKEND_PORT` | `8648` | Backend port used by the Vite dev proxy. | +| `HERMES_WEB_UI_FRONTEND_PORT` | `8649` | Frontend Vite dev server port. | ### CLI Commands diff --git a/README_zh.md b/README_zh.md index 73e5df0..e1ce9eb 100644 --- a/README_zh.md +++ b/README_zh.md @@ -237,22 +237,55 @@ Web UI 启动后端聊天能力时,会优先使用包含 `run_agent.py` 的源 ## Web UI 环境变量 -这些变量只用于配置 Hermes Web UI 自身。Provider API Key 和 Hermes Agent 相关设置仍通过 Hermes profile 管理。 +这些变量用于配置 Hermes Web UI、本地 Hermes runtime 集成以及开发/预览辅助能力。Provider API Key 和 Hermes Agent 相关设置通常仍通过 Hermes profile 管理;这里列出的变量是进程级覆盖项。 | 变量 | 默认值 | 说明 | |---|---|---| | `PORT` | `8648` | Web UI 监听端口。 | | `BIND_HOST` | `0.0.0.0` | Web UI 绑定地址。如需 IPv6,可显式设置为 `::`。 | | `HERMES_WEB_UI_HOME` | `~/.hermes-web-ui` | Web UI 数据目录,用于认证 token、登录凭据、日志、数据库和默认上传目录。兼容支持 `HERMES_WEBUI_STATE_DIR` 作为别名。 | +| `HERMES_WEBUI_STATE_DIR` | 未设置 | `HERMES_WEB_UI_HOME` 的兼容别名。 | | `UPLOAD_DIR` | `$HERMES_WEB_UI_HOME/upload` | 覆盖上传根目录。文件会保存在按 Profile 隔离的子目录下。 | | `CORS_ORIGINS` | `*` | Koa CORS origin 配置。 | | `AUTH_TOKEN` | 自动生成 | 显式指定 bearer token。未设置时,Web UI 会在 `HERMES_WEB_UI_HOME` 下自动生成。 | +| `AUTH_JWT_SECRET` | `AUTH_TOKEN` | 用户名/密码会话的 JWT 签名密钥覆盖。 | | `PROFILE` | `default` | 启动/默认 Hermes profile。运行时请求使用前端当前选择且当前账号有权限访问的 Profile。 | | `LOG_LEVEL` | `info` | Server 日志级别。 | | `BRIDGE_LOG_LEVEL` | `$LOG_LEVEL` 或 `info` | Bridge 日志级别。 | | `MAX_DOWNLOAD_SIZE` | `200MB` | 最大文件下载大小。 | | `MAX_EDIT_SIZE` | `10MB` | 最大可编辑文件大小。 | | `WORKSPACE_BASE` | `/opt/data/workspace` | Workspace 浏览根目录。 | +| `HERMES_HOME` | 平台默认值 | Hermes 数据目录。Windows 使用 `%LOCALAPPDATA%\hermes`;macOS/Linux 使用 `~/.hermes`。 | +| `HERMES_BIN` | `hermes` | 自定义 Hermes CLI 二进制路径。 | +| `HERMES_AGENT_ROOT` | 自动发现 | 包含 `run_agent.py` 的 Hermes Agent 源码目录。 | +| `HERMES_AGENT_BRIDGE_PYTHON` | 自动发现 | 用于启动 agent bridge 的 Python 解释器。 | +| `HERMES_AGENT_BRIDGE_UV` | 自动发现 | 可用时用于启动 agent bridge 的 `uv` 可执行文件。 | +| `UV` | 自动发现 | `uv` 可执行文件 fallback。 | +| `PYTHON` | 自动发现 | agent bridge 的 Python 可执行文件 fallback。 | +| `HERMES_AGENT_BRIDGE_ENDPOINT` | 平台默认值 | Agent bridge broker endpoint。Windows 默认 `tcp://127.0.0.1:18765`;macOS/Linux 默认 `ipc:///tmp/hermes-agent-bridge.sock`。 | +| `HERMES_AGENT_BRIDGE_TIMEOUT_MS` | `120000` | Node 请求 bridge broker 的响应超时。 | +| `HERMES_AGENT_BRIDGE_CONNECT_RETRY_MS` | `5000` | 连接 bridge socket 失败时的短重试窗口。 | +| `HERMES_AGENT_BRIDGE_STARTUP_TIMEOUT_MS` | `120000` | 等待 Python bridge ready 的超时。 | +| `HERMES_AGENT_BRIDGE_AUTO_RESTART` | 开启 | bridge broker 意外退出后是否自动重启;设为 `0`、`false`、`no` 或 `off` 可关闭。 | +| `HERMES_AGENT_BRIDGE_RESTART_DELAY_MS` | `1000` | bridge 自动重启退避的基础延迟。 | +| `HERMES_AGENT_BRIDGE_PLATFORM` | `cli` | 传给 Hermes Agent 的 platform 标识。 | +| `HERMES_AGENT_BRIDGE_WORKER_TRANSPORT` | 平台默认值 | Profile worker transport。设为 `tcp` 使用 loopback TCP;设为 `ipc`/`unix` 使用 Unix domain socket;默认 Windows TCP、macOS/Linux IPC。 | +| `HERMES_AGENT_BRIDGE_WORKER_PORT_BASE` | `18780` | TCP worker endpoint 起始端口。 | +| `HERMES_BRIDGE_PROVIDER` | profile/默认值 | bridge 运行时的 provider 覆盖。 | +| `HERMES_BRIDGE_TOOLSETS` | profile/默认值 | bridge 运行时的 toolset 覆盖。 | +| `HERMES_BRIDGE_MAX_TURNS` | profile/默认值 | bridge 运行时的最大轮数覆盖。 | +| `HERMES_BRIDGE_SUPPRESS_PLATFORM_HINT` | `cli` | 控制传给 Hermes Agent 的 bridge platform hint suppression。 | +| `HERMES_OPENROUTER_APP_REFERER` | `https://ekkolearnai.com` | bridge 运行发送给 OpenRouter 的 attribution referer。 | +| `HERMES_OPENROUTER_APP_TITLE` | `Hermes Web UI` | bridge 运行发送给 OpenRouter 的 attribution title。 | +| `HERMES_OPENROUTER_APP_CATEGORIES` | `cli-agent,personal-agent` | bridge 运行发送给 OpenRouter 的 attribution categories。 | +| `HERMES_WEB_UI_MANAGED_GATEWAY` | 由平台/运行环境决定 | 强制启用旧 gateway 进程托管;设为 `1`、`true`、`yes` 或 `on` 开启。 | +| `HERMES_WEB_UI_STOP_GATEWAYS_ON_SHUTDOWN` | 生产环境默认开启 | Web UI 关闭时是否同时停止托管的 gateway 进程;设为 `0` 或 `false` 可让 gateway 分离运行。 | +| `GATEWAY_HOST` | `127.0.0.1` | 旧 gateway 兼容配置中写入 profile 的默认 gateway host。 | +| `HERMES_WEB_UI_PREVIEW_REPO` | package repository | Version Preview 使用的 GitHub 仓库。 | +| `HERMES_WEB_UI_PREVIEW_AGENT_BRIDGE_TRANSPORT` | 平台默认值 | Version Preview broker transport。设为 `tcp` 可让预览环境在 macOS/Linux 上也使用 loopback TCP;未设置时会跟随 `HERMES_AGENT_BRIDGE_WORKER_TRANSPORT=tcp`。 | +| `HERMES_WEB_UI_PREVIEW_AGENT_BRIDGE_ENDPOINT` | 隔离的预览 endpoint | 直接覆盖 Version Preview 的 broker endpoint。 | +| `HERMES_WEB_UI_BACKEND_PORT` | `8648` | Vite dev proxy 使用的后端端口。 | +| `HERMES_WEB_UI_FRONTEND_PORT` | `8649` | 前端 Vite dev server 端口。 | ### CLI 命令 diff --git a/docs/cli-chat-sessions.md b/docs/cli-chat-sessions.md index 201e0d7..f1a19b8 100644 --- a/docs/cli-chat-sessions.md +++ b/docs/cli-chat-sessions.md @@ -436,6 +436,10 @@ chatRunServer.init() | 变量 | 说明 | |------|------| | `HERMES_AGENT_BRIDGE_ENDPOINT` | Bridge endpoint;Windows 默认 `tcp://127.0.0.1:18765`,macOS/Linux 默认 `ipc:///tmp/hermes-agent-bridge.sock` | +| `HERMES_AGENT_BRIDGE_WORKER_TRANSPORT` | profile worker endpoint transport;设为 `tcp` 使用 loopback TCP,设为 `ipc`/`unix` 使用 Unix domain socket;默认 Windows 使用 TCP,macOS/Linux 使用 IPC | +| `HERMES_AGENT_BRIDGE_WORKER_PORT_BASE` | TCP worker endpoint 起始端口,默认 `18780`;仅在 worker transport 为 TCP 时生效 | +| `HERMES_WEB_UI_PREVIEW_AGENT_BRIDGE_TRANSPORT` | Version Preview 的 bridge broker endpoint transport;设为 `tcp` 可让预览环境在 macOS/Linux 上也使用 loopback TCP;未设置时会跟随 `HERMES_AGENT_BRIDGE_WORKER_TRANSPORT=tcp` | +| `HERMES_WEB_UI_PREVIEW_AGENT_BRIDGE_ENDPOINT` | 直接覆盖 Version Preview 的 bridge broker endpoint;用于需要完全自定义预览 bridge 地址的部署 | | `HERMES_AGENT_BRIDGE_TIMEOUT_MS` | Node 等待 bridge 请求响应的超时,默认 `120000` ms | | `HERMES_AGENT_BRIDGE_CONNECT_RETRY_MS` | Node 连接 bridge socket 失败时的短重试窗口,默认 `5000` ms | | `HERMES_AGENT_BRIDGE_STARTUP_TIMEOUT_MS` | Node 等待 Python bridge ready 的超时,默认 `120000` ms | @@ -449,7 +453,7 @@ chatRunServer.init() | `HERMES_BRIDGE_MAX_TURNS` | 覆盖 bridge 最大轮数 | | `UV` | uv 可执行文件路径 fallback | -正常使用不需要配置这些变量。Bridge 支持多个用户/多个 profile 的运行并存;Web UI 的 Hermes Profile 切换不会重启 bridge 或销毁其他正在运行的任务。Windows 下如果默认 TCP 端口被旧 bridge/broker/worker 占用,新 bridge 会先按端口杀掉旧进程树,再用同一个 endpoint 重建。 +正常使用不需要配置这些变量。Bridge 支持多个用户/多个 profile 的运行并存;Web UI 的 Hermes Profile 切换不会重启 bridge 或销毁其他正在运行的任务。`HERMES_AGENT_BRIDGE_ENDPOINT` 控制 Node 与 Python bridge broker 的连接地址;`HERMES_AGENT_BRIDGE_WORKER_TRANSPORT` 控制 broker 与每个 profile worker 的连接方式。macOS/Linux 默认仍使用 IPC;如果 Electron、沙盒或安全软件环境下 IPC 不稳定,可以设置 `HERMES_AGENT_BRIDGE_WORKER_TRANSPORT=tcp` 切到 loopback TCP。Version Preview 默认继续使用独立的 broker endpoint,也会为 TCP worker 使用独立端口段,避免和正式实例共享端口池;如需让 Preview 的 broker 在 macOS/Linux 上也走 TCP,可设置 `HERMES_WEB_UI_PREVIEW_AGENT_BRIDGE_TRANSPORT=tcp`,未设置时会跟随 `HERMES_AGENT_BRIDGE_WORKER_TRANSPORT=tcp`。Windows 下如果默认 TCP 端口被旧 bridge/broker/worker 占用,新 bridge 会先按端口杀掉旧进程树,再用同一个 endpoint 重建。 Windows 首次启动慢时可以临时放大: diff --git a/packages/server/src/controllers/update.ts b/packages/server/src/controllers/update.ts index dfa06b9..1ca3e18 100644 --- a/packages/server/src/controllers/update.ts +++ b/packages/server/src/controllers/update.ts @@ -12,6 +12,9 @@ const PREVIEW_HOME_DIR_NAME = 'hermes-web-ui-pereview-home' const PREVIEW_BACKEND_PORT = 8650 const PREVIEW_FRONTEND_PORT = 8651 const PREVIEW_AGENT_BRIDGE_PORT = 18650 +const PREVIEW_AGENT_BRIDGE_WORKER_PORT_BASE = 19650 +const PREVIEW_AGENT_BRIDGE_ENDPOINT_ENV = 'HERMES_WEB_UI_PREVIEW_AGENT_BRIDGE_ENDPOINT' +const PREVIEW_AGENT_BRIDGE_TRANSPORT_ENV = 'HERMES_WEB_UI_PREVIEW_AGENT_BRIDGE_TRANSPORT' const PREVIEW_FRONTEND_URL = `http://localhost:${PREVIEW_FRONTEND_PORT}` const PREVIEW_TAG_REF_PATTERN = /^[A-Za-z0-9._/-]+$/ const PREVIEW_MAIN_REF = 'main' @@ -207,12 +210,43 @@ function getPreviewHomeDir() { return join(getWebUiHome(), PREVIEW_HOME_DIR_NAME) } +function normalizePreviewAgentBridgeTransport(value: string | undefined) { + const transport = value?.trim().toLowerCase() + return transport && ['tcp', 'ipc', 'unix'].includes(transport) ? transport : '' +} + function getPreviewAgentBridgeEndpoint() { - return process.platform === 'win32' + const configured = process.env[PREVIEW_AGENT_BRIDGE_ENDPOINT_ENV]?.trim() + if (configured) return configured + + const transport = normalizePreviewAgentBridgeTransport(process.env[PREVIEW_AGENT_BRIDGE_TRANSPORT_ENV]) + || normalizePreviewAgentBridgeTransport(process.env.HERMES_AGENT_BRIDGE_WORKER_TRANSPORT) + const useTcp = transport ? transport === 'tcp' : process.platform === 'win32' + return useTcp ? `tcp://127.0.0.1:${PREVIEW_AGENT_BRIDGE_PORT}` : `ipc://${join(getPreviewHomeDir(), 'agent-bridge.sock')}` } +function getTcpEndpointPort(endpoint: string): number | null { + try { + const url = new URL(endpoint) + if (url.protocol !== 'tcp:') return null + const port = Number(url.port) + return Number.isInteger(port) && port > 0 && port <= 65535 ? port : null + } catch { + return null + } +} + +function getPreviewListeningPorts() { + const agentBridgePort = getTcpEndpointPort(getPreviewAgentBridgeEndpoint()) + return [ + PREVIEW_BACKEND_PORT, + PREVIEW_FRONTEND_PORT, + ...(agentBridgePort ? [agentBridgePort] : []), + ] +} + function getPreviewPackagePath() { return join(getPreviewDir(), 'package.json') } @@ -315,11 +349,7 @@ function parsePidLines(output: string): number[] { } function getPreviewListeningPids(): number[] { - const ports = [ - PREVIEW_BACKEND_PORT, - PREVIEW_FRONTEND_PORT, - ...(process.platform === 'win32' ? [PREVIEW_AGENT_BRIDGE_PORT] : []), - ] + const ports = getPreviewListeningPorts() const pids = new Set() if (process.platform === 'win32') { @@ -367,11 +397,7 @@ function getUnixProcessGroupId(pid: number): number | null { } async function assertPreviewPortsAvailable() { - const ports = [ - PREVIEW_BACKEND_PORT, - PREVIEW_FRONTEND_PORT, - ...(process.platform === 'win32' ? [PREVIEW_AGENT_BRIDGE_PORT] : []), - ] + const ports = getPreviewListeningPorts() const checks = await Promise.all(ports.map(port => isPortAvailable(port))) const busy = ports.filter((_, index) => !checks[index]) @@ -964,6 +990,7 @@ export async function startPreview(ctx: any) { HERMES_WEB_UI_HOME: getPreviewHomeDir(), HERMES_WEBUI_STATE_DIR: getPreviewHomeDir(), HERMES_AGENT_BRIDGE_ENDPOINT: getPreviewAgentBridgeEndpoint(), + HERMES_AGENT_BRIDGE_WORKER_PORT_BASE: String(PREVIEW_AGENT_BRIDGE_WORKER_PORT_BASE), AUTH_TOKEN: '', HERMES_WEB_UI_BACKEND_PORT: String(PREVIEW_BACKEND_PORT), HERMES_WEB_UI_FRONTEND_PORT: String(PREVIEW_FRONTEND_PORT), diff --git a/packages/server/src/services/hermes/agent-bridge/README.md b/packages/server/src/services/hermes/agent-bridge/README.md index cf98e83..69209fb 100644 --- a/packages/server/src/services/hermes/agent-bridge/README.md +++ b/packages/server/src/services/hermes/agent-bridge/README.md @@ -30,6 +30,13 @@ Override with: HERMES_AGENT_BRIDGE_ENDPOINT=tcp://127.0.0.1:8765 python packages/server/src/services/hermes/agent-bridge/hermes_bridge.py ``` +Profile workers use the same platform defaults: TCP on Windows and IPC on +macOS/Linux. Override worker transport with: + +```bash +HERMES_AGENT_BRIDGE_WORKER_TRANSPORT=tcp HERMES_AGENT_BRIDGE_WORKER_PORT_BASE=18780 python packages/server/src/services/hermes/agent-bridge/hermes_bridge.py +``` + The service discovers Hermes Agent in this order: 1. `--agent-root` diff --git a/packages/server/src/services/hermes/agent-bridge/hermes_bridge.py b/packages/server/src/services/hermes/agent-bridge/hermes_bridge.py index e47969b..c76387c 100755 --- a/packages/server/src/services/hermes/agent-bridge/hermes_bridge.py +++ b/packages/server/src/services/hermes/agent-bridge/hermes_bridge.py @@ -2416,7 +2416,9 @@ class WorkerProcess: def _worker_endpoint(key: str, namespace: str | None = None) -> str: namespace_key = f"{namespace or ''}\0{key}" safe = hashlib.sha256(namespace_key.encode("utf-8")).hexdigest()[:16] - if os.name == "nt": + transport = os.environ.get("HERMES_AGENT_BRIDGE_WORKER_TRANSPORT", "").strip().lower() + use_tcp = transport == "tcp" or (transport not in {"ipc", "unix"} and os.name == "nt") + if use_tcp: port_base = int(os.environ.get("HERMES_AGENT_BRIDGE_WORKER_PORT_BASE", "18780")) return f"tcp://127.0.0.1:{port_base + int(safe[:4], 16) % 1000}" root = Path(tempfile.gettempdir()) / "hermes-agent-bridge-workers" diff --git a/packages/website/src/i18n/en.ts b/packages/website/src/i18n/en.ts index 544a500..edcb71b 100644 --- a/packages/website/src/i18n/en.ts +++ b/packages/website/src/i18n/en.ts @@ -133,17 +133,56 @@ export default { envVars: { title: 'Environment Variables', rows: [ - ['AUTH_TOKEN', 'Custom auth token (overrides auto-generated)'], ['PORT', 'Server listen port (default: 8648)'], ['BIND_HOST', 'Server bind host (default: 0.0.0.0). Set :: explicitly to enable IPv6 listening.'], + ['HERMES_WEB_UI_HOME', 'Web UI data home for auth token, credentials, logs, DB, and default uploads'], + ['HERMES_WEBUI_STATE_DIR', 'Compatibility alias for HERMES_WEB_UI_HOME'], ['UPLOAD_DIR', 'Custom upload root. Uploaded files are stored below profile-scoped subdirectories.'], ['CORS_ORIGINS', 'CORS origin config (default: *)'], - ['HERMES_BIN', 'Custom path to hermes CLI binary'], + ['AUTH_TOKEN', 'Custom bearer token; overrides the auto-generated token'], + ['AUTH_JWT_SECRET', 'JWT signing secret override for username/password sessions'], + ['PROFILE', 'Startup/default Hermes profile'], + ['LOG_LEVEL', 'Server log level'], + ['BRIDGE_LOG_LEVEL', 'Bridge log level'], + ['MAX_DOWNLOAD_SIZE', 'Maximum file download size'], + ['MAX_EDIT_SIZE', 'Maximum editable file size'], + ['WORKSPACE_BASE', 'Base directory for workspace browsing'], + ['HERMES_HOME', 'Hermes data home'], + ['HERMES_BIN', 'Custom Hermes CLI binary path'], + ['HERMES_AGENT_ROOT', 'Hermes Agent source checkout containing run_agent.py'], + ['HERMES_AGENT_BRIDGE_PYTHON', 'Python interpreter used to launch the agent bridge'], + ['HERMES_AGENT_BRIDGE_UV', 'uv executable used to launch the agent bridge when available'], + ['UV', 'Fallback uv executable path'], + ['PYTHON', 'Fallback Python executable for the agent bridge'], + ['HERMES_AGENT_BRIDGE_ENDPOINT', 'Agent bridge broker endpoint. Windows defaults to tcp://127.0.0.1:18765; macOS/Linux defaults to ipc:///tmp/hermes-agent-bridge.sock'], + ['HERMES_AGENT_BRIDGE_TIMEOUT_MS', 'Timeout for Node requests to the bridge broker'], + ['HERMES_AGENT_BRIDGE_CONNECT_RETRY_MS', 'Short retry window for connecting to the bridge socket'], + ['HERMES_AGENT_BRIDGE_STARTUP_TIMEOUT_MS', 'Timeout while waiting for the Python bridge to become ready'], + ['HERMES_AGENT_BRIDGE_AUTO_RESTART', 'Auto-restart the bridge broker after unexpected exit; set 0/false/no/off to disable'], + ['HERMES_AGENT_BRIDGE_RESTART_DELAY_MS', 'Base delay for bridge auto-restart backoff'], + ['HERMES_AGENT_BRIDGE_PLATFORM', 'Platform identity passed to Hermes Agent'], + ['HERMES_AGENT_BRIDGE_WORKER_TRANSPORT', 'Profile worker endpoint transport. Set tcp for loopback TCP, or ipc/unix for Unix domain sockets; defaults to Windows TCP and macOS/Linux IPC'], + ['HERMES_AGENT_BRIDGE_WORKER_PORT_BASE', 'Base port for TCP worker endpoints (default: 18780). Version Preview uses an isolated 19650 port range'], + ['HERMES_BRIDGE_PROVIDER', 'Provider override for bridge runs'], + ['HERMES_BRIDGE_TOOLSETS', 'Toolset override for bridge runs'], + ['HERMES_BRIDGE_MAX_TURNS', 'Maximum turn override for bridge runs'], + ['HERMES_BRIDGE_SUPPRESS_PLATFORM_HINT', 'Controls bridge platform hint suppression passed to Hermes Agent'], + ['HERMES_OPENROUTER_APP_REFERER', 'OpenRouter attribution referer sent by bridge runs'], + ['HERMES_OPENROUTER_APP_TITLE', 'OpenRouter attribution title sent by bridge runs'], + ['HERMES_OPENROUTER_APP_CATEGORIES', 'OpenRouter attribution categories sent by bridge runs'], + ['HERMES_WEB_UI_MANAGED_GATEWAY', 'Force managed legacy gateway process handling'], + ['HERMES_WEB_UI_STOP_GATEWAYS_ON_SHUTDOWN', 'Controls whether Web UI shutdown also stops managed gateway processes'], + ['GATEWAY_HOST', 'Default gateway host written into profile config for legacy gateway compatibility'], + ['HERMES_WEB_UI_PREVIEW_REPO', 'GitHub repository used by Version Preview'], + ['HERMES_WEB_UI_PREVIEW_AGENT_BRIDGE_TRANSPORT', 'Version Preview broker endpoint transport. Set tcp to use loopback TCP for Preview on macOS/Linux; when unset, Preview follows HERMES_AGENT_BRIDGE_WORKER_TRANSPORT=tcp'], + ['HERMES_WEB_UI_PREVIEW_AGENT_BRIDGE_ENDPOINT', 'Directly overrides the Version Preview broker endpoint for deployments that need a fully custom Preview bridge address'], + ['HERMES_WEB_UI_BACKEND_PORT', 'Backend port used by the Vite dev proxy'], + ['HERMES_WEB_UI_FRONTEND_PORT', 'Frontend Vite dev server port'], ], }, gateway: { title: 'Agent Bridge Runtime', - content: 'Chat runs are handled through the Hermes agent bridge, which runs alongside the Web UI server and talks directly to the Hermes Agent runtime. Switching the frontend Hermes Profile changes later request context only; it does not restart the bridge or clear other running tasks.', + content: 'Chat runs are handled through the Hermes agent bridge, which runs alongside the Web UI server and talks directly to the Hermes Agent runtime. HERMES_AGENT_BRIDGE_ENDPOINT controls the Node-to-broker address, while HERMES_AGENT_BRIDGE_WORKER_TRANSPORT controls the broker-to-profile-worker transport. Switching the frontend Hermes Profile changes later request context only; it does not restart the bridge or clear other running tasks.', }, profiles: { title: 'Profiles', diff --git a/packages/website/src/i18n/zh.ts b/packages/website/src/i18n/zh.ts index b0fbde3..037cadf 100644 --- a/packages/website/src/i18n/zh.ts +++ b/packages/website/src/i18n/zh.ts @@ -133,17 +133,56 @@ export default { envVars: { title: '环境变量', rows: [ - ['AUTH_TOKEN', '自定义认证令牌(覆盖自动生成的令牌)'], ['PORT', '服务器监听端口(默认:8648)'], ['BIND_HOST', '服务器绑定地址(默认:0.0.0.0)。如需 IPv6,请显式设置为 ::。'], + ['HERMES_WEB_UI_HOME', 'Web UI 数据目录,用于认证 token、登录凭据、日志、数据库和默认上传目录'], + ['HERMES_WEBUI_STATE_DIR', 'HERMES_WEB_UI_HOME 的兼容别名'], ['UPLOAD_DIR', '自定义上传根目录。文件会保存在按 Profile 隔离的子目录下'], ['CORS_ORIGINS', 'CORS 来源配置(默认:*)'], - ['HERMES_BIN', '自定义 hermes CLI 二进制路径'], + ['AUTH_TOKEN', '自定义 bearer token,会覆盖自动生成的 token'], + ['AUTH_JWT_SECRET', '用户名/密码会话的 JWT 签名密钥覆盖'], + ['PROFILE', '启动/默认 Hermes profile'], + ['LOG_LEVEL', 'Server 日志级别'], + ['BRIDGE_LOG_LEVEL', 'Bridge 日志级别'], + ['MAX_DOWNLOAD_SIZE', '最大文件下载大小'], + ['MAX_EDIT_SIZE', '最大可编辑文件大小'], + ['WORKSPACE_BASE', 'Workspace 浏览根目录'], + ['HERMES_HOME', 'Hermes 数据目录'], + ['HERMES_BIN', '自定义 Hermes CLI 二进制路径'], + ['HERMES_AGENT_ROOT', '包含 run_agent.py 的 Hermes Agent 源码目录'], + ['HERMES_AGENT_BRIDGE_PYTHON', '用于启动 agent bridge 的 Python 解释器'], + ['HERMES_AGENT_BRIDGE_UV', '可用时用于启动 agent bridge 的 uv 可执行文件'], + ['UV', 'uv 可执行文件 fallback'], + ['PYTHON', 'agent bridge 的 Python 可执行文件 fallback'], + ['HERMES_AGENT_BRIDGE_ENDPOINT', 'Agent bridge broker endpoint。Windows 默认 tcp://127.0.0.1:18765;macOS/Linux 默认 ipc:///tmp/hermes-agent-bridge.sock'], + ['HERMES_AGENT_BRIDGE_TIMEOUT_MS', 'Node 请求 bridge broker 的响应超时'], + ['HERMES_AGENT_BRIDGE_CONNECT_RETRY_MS', '连接 bridge socket 失败时的短重试窗口'], + ['HERMES_AGENT_BRIDGE_STARTUP_TIMEOUT_MS', '等待 Python bridge ready 的超时'], + ['HERMES_AGENT_BRIDGE_AUTO_RESTART', 'bridge broker 意外退出后是否自动重启;设为 0/false/no/off 可关闭'], + ['HERMES_AGENT_BRIDGE_RESTART_DELAY_MS', 'bridge 自动重启退避的基础延迟'], + ['HERMES_AGENT_BRIDGE_PLATFORM', '传给 Hermes Agent 的 platform 标识'], + ['HERMES_AGENT_BRIDGE_WORKER_TRANSPORT', 'profile worker endpoint transport。设为 tcp 使用 loopback TCP;设为 ipc/unix 使用 Unix domain socket;默认 Windows TCP、macOS/Linux IPC'], + ['HERMES_AGENT_BRIDGE_WORKER_PORT_BASE', 'TCP worker endpoint 起始端口(默认:18780)。Version Preview 会使用独立端口段 19650'], + ['HERMES_BRIDGE_PROVIDER', 'bridge 运行时的 provider 覆盖'], + ['HERMES_BRIDGE_TOOLSETS', 'bridge 运行时的 toolset 覆盖'], + ['HERMES_BRIDGE_MAX_TURNS', 'bridge 运行时的最大轮数覆盖'], + ['HERMES_BRIDGE_SUPPRESS_PLATFORM_HINT', '控制传给 Hermes Agent 的 bridge platform hint suppression'], + ['HERMES_OPENROUTER_APP_REFERER', 'bridge 运行发送给 OpenRouter 的 attribution referer'], + ['HERMES_OPENROUTER_APP_TITLE', 'bridge 运行发送给 OpenRouter 的 attribution title'], + ['HERMES_OPENROUTER_APP_CATEGORIES', 'bridge 运行发送给 OpenRouter 的 attribution categories'], + ['HERMES_WEB_UI_MANAGED_GATEWAY', '强制启用旧 gateway 进程托管'], + ['HERMES_WEB_UI_STOP_GATEWAYS_ON_SHUTDOWN', 'Web UI 关闭时是否同时停止托管的 gateway 进程'], + ['GATEWAY_HOST', '旧 gateway 兼容配置中写入 profile 的默认 gateway host'], + ['HERMES_WEB_UI_PREVIEW_REPO', 'Version Preview 使用的 GitHub 仓库'], + ['HERMES_WEB_UI_PREVIEW_AGENT_BRIDGE_TRANSPORT', 'Version Preview 的 broker endpoint transport。设为 tcp 可让预览环境在 macOS/Linux 上也使用 loopback TCP;未设置时会跟随 HERMES_AGENT_BRIDGE_WORKER_TRANSPORT=tcp'], + ['HERMES_WEB_UI_PREVIEW_AGENT_BRIDGE_ENDPOINT', '直接覆盖 Version Preview 的 broker endpoint;用于需要完全自定义预览 bridge 地址的部署'], + ['HERMES_WEB_UI_BACKEND_PORT', 'Vite dev proxy 使用的后端端口'], + ['HERMES_WEB_UI_FRONTEND_PORT', '前端 Vite dev server 端口'], ], }, gateway: { title: 'Agent Bridge 运行时', - content: '聊天运行通过 Hermes agent bridge 处理。它随 Web UI 服务一起运行,并直接连接 Hermes Agent runtime。前端切换 Hermes Profile 只影响后续请求上下文,不会重启 bridge 或清理其他正在运行的任务。', + content: '聊天运行通过 Hermes agent bridge 处理。它随 Web UI 服务一起运行,并直接连接 Hermes Agent runtime。HERMES_AGENT_BRIDGE_ENDPOINT 控制 Node 与 bridge broker 的连接地址;HERMES_AGENT_BRIDGE_WORKER_TRANSPORT 控制 broker 与各 Profile worker 的连接方式。前端切换 Hermes Profile 只影响后续请求上下文,不会重启 bridge 或清理其他正在运行的任务。', }, profiles: { title: '配置文件', diff --git a/tests/server/agent-bridge-python-concurrency.test.ts b/tests/server/agent-bridge-python-concurrency.test.ts index 13e2059..d71cf5a 100644 --- a/tests/server/agent-bridge-python-concurrency.test.ts +++ b/tests/server/agent-bridge-python-concurrency.test.ts @@ -470,6 +470,35 @@ assert prod_worker.endpoint != preview_worker.endpoint `) }) + it('allows worker transport to be selected with environment variables', () => { + runPython(String.raw` +${harness} + +os.environ.pop("HERMES_AGENT_BRIDGE_WORKER_TRANSPORT", None) +os.environ.pop("HERMES_AGENT_BRIDGE_WORKER_PORT_BASE", None) + +default_endpoint = bridge._worker_endpoint("default", "ipc:///tmp/hermes-agent-bridge.sock") +if os.name == "nt": + assert default_endpoint.startswith("tcp://127.0.0.1:") +else: + assert default_endpoint.startswith("ipc://") + +os.environ["HERMES_AGENT_BRIDGE_WORKER_TRANSPORT"] = "tcp" +os.environ["HERMES_AGENT_BRIDGE_WORKER_PORT_BASE"] = "19650" +tcp_endpoint = bridge._worker_endpoint("default", "ipc:///tmp/hermes-agent-bridge.sock") +assert tcp_endpoint.startswith("tcp://127.0.0.1:") +assert int(tcp_endpoint.rsplit(":", 1)[1]) >= 19650 +assert int(tcp_endpoint.rsplit(":", 1)[1]) < 20650 + +os.environ["HERMES_AGENT_BRIDGE_WORKER_TRANSPORT"] = "ipc" +ipc_endpoint = bridge._worker_endpoint("default", "ipc:///tmp/hermes-agent-bridge.sock") +assert ipc_endpoint.startswith("ipc://") + +os.environ.pop("HERMES_AGENT_BRIDGE_WORKER_TRANSPORT", None) +os.environ.pop("HERMES_AGENT_BRIDGE_WORKER_PORT_BASE", None) +`) + }) + it('restores approval env and clears handlers when a run fails', () => { runPython(String.raw` ${harness}