feat: add docker-compose deployment and harden gateway startup
- add docker-compose setup with hermes-agent + hermes-webui - make runtime config env-driven (compose vars + HERMES_BIN) - improve gateway startup/restart resilience in docker - make base image configurable via BASE_IMAGE/HERMES_AGENT_IMAGE Closes https://github.com/EKKOLearnAI/hermes-web-ui/issues/14
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
hermes_data
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
@@ -18,6 +18,7 @@ ROADMAP.md
|
|||||||
packages/server/data/
|
packages/server/data/
|
||||||
packages/server/node_modules/
|
packages/server/node_modules/
|
||||||
.hermes-web-ui/
|
.hermes-web-ui/
|
||||||
|
hermes_data/
|
||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
.vscode/*
|
.vscode/*
|
||||||
|
|||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
ARG BASE_IMAGE=nousresearch/hermes-agent:latest
|
||||||
|
FROM ${BASE_IMAGE}
|
||||||
|
|
||||||
|
USER root
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
ca-certificates \
|
||||||
|
curl \
|
||||||
|
gnupg \
|
||||||
|
python3 \
|
||||||
|
python3-yaml \
|
||||||
|
make \
|
||||||
|
g++ \
|
||||||
|
&& curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
|
||||||
|
&& apt-get install -y --no-install-recommends nodejs \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN npm run build && npm prune --omit=dev
|
||||||
|
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
ENV HOME=/home/agent
|
||||||
|
ENV HERMES_HOME=/home/agent/.hermes
|
||||||
|
|
||||||
|
EXPOSE 6060
|
||||||
|
|
||||||
|
CMD ["node", "dist/server/index.js"]
|
||||||
@@ -144,6 +144,32 @@ hermes-web-ui start
|
|||||||
|
|
||||||
> WSL auto-detects and uses `hermes gateway run` for background startup (no launchd/systemd).
|
> WSL auto-detects and uses `hermes gateway run` for background startup (no launchd/systemd).
|
||||||
|
|
||||||
|
### Docker Compose
|
||||||
|
|
||||||
|
Run Web UI together with Hermes Agent:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d --build hermes-agent hermes-webui
|
||||||
|
docker compose logs -f hermes-webui
|
||||||
|
```
|
||||||
|
|
||||||
|
Open **http://localhost:6060**
|
||||||
|
|
||||||
|
- Persistent Hermes data is stored in `./hermes_data`
|
||||||
|
- The web UI service is built from this repository's `Dockerfile`
|
||||||
|
- All runtime settings are environment-variable driven in `docker-compose.yml`
|
||||||
|
|
||||||
|
Override compose variables directly from command line (no `.env` file required):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PORT=16060 \
|
||||||
|
UPSTREAM=http://127.0.0.1:8642 \
|
||||||
|
HERMES_BIN=/opt/hermes/.venv/bin/hermes \
|
||||||
|
docker compose up -d --build hermes-agent hermes-webui
|
||||||
|
```
|
||||||
|
|
||||||
|
For detailed notes and troubleshooting, see [`docs/docker.md`](./docs/docker.md).
|
||||||
|
|
||||||
### CLI Commands
|
### CLI Commands
|
||||||
|
|
||||||
| Command | Description |
|
| Command | Description |
|
||||||
|
|||||||
@@ -145,6 +145,32 @@ hermes-web-ui start
|
|||||||
|
|
||||||
> WSL 会自动检测并使用 `hermes gateway run` 进行后台启动(无需 launchd/systemd)。
|
> WSL 会自动检测并使用 `hermes gateway run` 进行后台启动(无需 launchd/systemd)。
|
||||||
|
|
||||||
|
### Docker Compose
|
||||||
|
|
||||||
|
使用仓库内置的 compose 文件联合运行 Hermes Agent + Web UI:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d --build hermes-agent hermes-webui
|
||||||
|
docker compose logs -f hermes-webui
|
||||||
|
```
|
||||||
|
|
||||||
|
打开 **http://localhost:6060**
|
||||||
|
|
||||||
|
- Hermes 持久化数据目录:`./hermes_data`
|
||||||
|
- Web UI 服务镜像由本仓库 `Dockerfile` 本地构建
|
||||||
|
- 运行参数全部由 `docker-compose.yml` 环境变量驱动
|
||||||
|
|
||||||
|
可直接在命令行覆盖 compose 变量(不依赖 `.env` 文件):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PORT=16060 \
|
||||||
|
UPSTREAM=http://127.0.0.1:8642 \
|
||||||
|
HERMES_BIN=/opt/hermes/.venv/bin/hermes \
|
||||||
|
docker compose up -d --build hermes-agent hermes-webui
|
||||||
|
```
|
||||||
|
|
||||||
|
更详细的说明与排错见:[`docs/docker.md`](./docs/docker.md)
|
||||||
|
|
||||||
### CLI 命令
|
### CLI 命令
|
||||||
|
|
||||||
| 命令 | 说明 |
|
| 命令 | 说明 |
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
services:
|
||||||
|
hermes-agent:
|
||||||
|
image: ${HERMES_AGENT_IMAGE:-nousresearch/hermes-agent:latest}
|
||||||
|
container_name: ${HERMES_AGENT_CONTAINER_NAME:-hermes-agent}
|
||||||
|
volumes:
|
||||||
|
- ${HERMES_DATA_DIR:-./hermes_data}:/home/agent/.hermes
|
||||||
|
- hermes-agent-src:/opt/hermes
|
||||||
|
environment:
|
||||||
|
- HERMES_HOME=/home/agent/.hermes
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
hermes-webui:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
args:
|
||||||
|
BASE_IMAGE: ${HERMES_AGENT_IMAGE:-nousresearch/hermes-agent:latest}
|
||||||
|
image: ${WEBUI_IMAGE:-hermes-web-ui-local:latest}
|
||||||
|
container_name: ${WEBUI_CONTAINER_NAME:-hermes-webui}
|
||||||
|
entrypoint: ["node", "dist/server/index.js"]
|
||||||
|
depends_on:
|
||||||
|
- hermes-agent
|
||||||
|
ports:
|
||||||
|
- "${PORT:-6060}:${PORT:-6060}"
|
||||||
|
volumes:
|
||||||
|
- ${HERMES_DATA_DIR:-./hermes_data}:/home/agent/.hermes
|
||||||
|
- hermes-agent-src:/opt/hermes
|
||||||
|
environment:
|
||||||
|
- PORT=${PORT:-6060}
|
||||||
|
- UPSTREAM=${UPSTREAM:-http://127.0.0.1:8642}
|
||||||
|
- HERMES_HOME=/home/agent/.hermes
|
||||||
|
- HERMES_BIN=${HERMES_BIN:-/opt/hermes/.venv/bin/hermes}
|
||||||
|
- AUTH_DISABLED=${AUTH_DISABLED:-true}
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
hermes-agent-src:
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
# Docker Compose Guide
|
||||||
|
|
||||||
|
This repository ships an environment-variable driven Docker Compose setup.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d --build hermes-agent hermes-webui
|
||||||
|
docker compose logs -f hermes-webui
|
||||||
|
```
|
||||||
|
|
||||||
|
Open: `http://localhost:6060`
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
All key runtime settings are configured from compose variables.
|
||||||
|
This compose file runs two services together:
|
||||||
|
|
||||||
|
- `hermes-agent` (image: `nousresearch/hermes-agent`)
|
||||||
|
- `hermes-webui` (built from this repository)
|
||||||
|
|
||||||
|
Compose mapping highlights:
|
||||||
|
|
||||||
|
- Host/browser port: `${PORT}:${PORT}`
|
||||||
|
- Server `PORT` is set from `${PORT}`
|
||||||
|
- Upstream is set from `${UPSTREAM}`
|
||||||
|
- Hermes CLI binary is set from `${HERMES_BIN}`
|
||||||
|
- Hermes base image is set from `${HERMES_AGENT_IMAGE}` (used by both `hermes-agent` and webui build base)
|
||||||
|
|
||||||
|
Override variables directly from shell when running compose:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PORT=16060 \
|
||||||
|
UPSTREAM=http://127.0.0.1:8642 \
|
||||||
|
HERMES_BIN=/opt/hermes/.venv/bin/hermes \
|
||||||
|
docker compose up -d --build hermes-agent hermes-webui
|
||||||
|
```
|
||||||
|
|
||||||
|
## Data Persistence
|
||||||
|
|
||||||
|
- Hermes runtime data persists in `${HERMES_DATA_DIR}`.
|
||||||
|
- Default path is `./hermes_data`.
|
||||||
|
|
||||||
|
## Code Runtime Behavior
|
||||||
|
|
||||||
|
- Server upstream comes from `UPSTREAM` env (`packages/server/src/config.ts`).
|
||||||
|
- Hermes CLI binary comes from `HERMES_BIN` env (`packages/server/src/services/hermes-cli.ts`).
|
||||||
|
- If `HERMES_BIN` is not provided, code falls back to `hermes` in `PATH`.
|
||||||
|
|
||||||
|
## Common Operations
|
||||||
|
|
||||||
|
Recreate webui:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d --no-deps --force-recreate hermes-webui
|
||||||
|
```
|
||||||
|
|
||||||
|
Stop:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose down
|
||||||
|
```
|
||||||
@@ -268,6 +268,17 @@ async function ensureApiServerConfig() {
|
|||||||
|
|
||||||
async function ensureGatewayRunning() {
|
async function ensureGatewayRunning() {
|
||||||
const upstream = config.upstream.replace(/\/$/, '')
|
const upstream = config.upstream.replace(/\/$/, '')
|
||||||
|
const waitForGatewayReady = async (timeoutMs: number = 15000) => {
|
||||||
|
const deadline = Date.now() + timeoutMs
|
||||||
|
while (Date.now() < deadline) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${upstream}/health`, { signal: AbortSignal.timeout(2000) })
|
||||||
|
if (res.ok) return true
|
||||||
|
} catch { }
|
||||||
|
await new Promise(r => setTimeout(r, 300))
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${upstream}/health`, { signal: AbortSignal.timeout(5000) })
|
const res = await fetch(`${upstream}/health`, { signal: AbortSignal.timeout(5000) })
|
||||||
@@ -279,16 +290,14 @@ async function ensureGatewayRunning() {
|
|||||||
try {
|
try {
|
||||||
// 👉 关键:保存 PID
|
// 👉 关键:保存 PID
|
||||||
gatewayPid = await startGatewayBackground()
|
gatewayPid = await startGatewayBackground()
|
||||||
|
if (await waitForGatewayReady()) {
|
||||||
await new Promise(r => setTimeout(r, 3000))
|
|
||||||
|
|
||||||
const res = await fetch(`${upstream}/health`, { signal: AbortSignal.timeout(5000) })
|
|
||||||
if (res.ok) {
|
|
||||||
console.log(`✓ Gateway started (PID: ${gatewayPid})`)
|
console.log(`✓ Gateway started (PID: ${gatewayPid})`)
|
||||||
|
} else {
|
||||||
|
console.error('gateway start failed: timed out waiting for health')
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error('gateway start failed:', err.message)
|
console.error('gateway start failed:', err.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bootstrap()
|
bootstrap()
|
||||||
|
|||||||
@@ -1,6 +1,32 @@
|
|||||||
import type { Context } from 'koa'
|
import type { Context } from 'koa'
|
||||||
import { config } from '../../config'
|
import { config } from '../../config'
|
||||||
|
|
||||||
|
function isTransientGatewayError(err: any): boolean {
|
||||||
|
const msg = String(err?.message || '')
|
||||||
|
const causeCode = String(err?.cause?.code || '')
|
||||||
|
return (
|
||||||
|
causeCode === 'ECONNREFUSED' ||
|
||||||
|
causeCode === 'ECONNRESET' ||
|
||||||
|
/ECONNREFUSED|ECONNRESET|fetch failed|socket hang up/i.test(msg)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function waitForGatewayReady(upstream: string, timeoutMs: number = 5000): Promise<boolean> {
|
||||||
|
const deadline = Date.now() + timeoutMs
|
||||||
|
const healthUrl = `${upstream}/health`
|
||||||
|
while (Date.now() < deadline) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(healthUrl, {
|
||||||
|
method: 'GET',
|
||||||
|
signal: AbortSignal.timeout(1200),
|
||||||
|
})
|
||||||
|
if (res.ok) return true
|
||||||
|
} catch { }
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 250))
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
export async function proxy(ctx: Context) {
|
export async function proxy(ctx: Context) {
|
||||||
const upstream = config.upstream.replace(/\/$/, '')
|
const upstream = config.upstream.replace(/\/$/, '')
|
||||||
// Rewrite path for upstream gateway:
|
// Rewrite path for upstream gateway:
|
||||||
@@ -36,11 +62,23 @@ export async function proxy(ctx: Context) {
|
|||||||
body = (ctx as any).request.rawBody as string | undefined
|
body = (ctx as any).request.rawBody as string | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await fetch(url, {
|
const requestInit: RequestInit = {
|
||||||
method: ctx.req.method,
|
method: ctx.req.method,
|
||||||
headers,
|
headers,
|
||||||
body,
|
body,
|
||||||
})
|
}
|
||||||
|
|
||||||
|
let res: Response
|
||||||
|
try {
|
||||||
|
res = await fetch(url, requestInit)
|
||||||
|
} catch (err: any) {
|
||||||
|
// Gateway may be restarting; wait briefly and retry once.
|
||||||
|
if (isTransientGatewayError(err) && await waitForGatewayReady(upstream)) {
|
||||||
|
res = await fetch(url, requestInit)
|
||||||
|
} else {
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set response headers
|
// Set response headers
|
||||||
const resHeaders: Record<string, string> = {}
|
const resHeaders: Record<string, string> = {}
|
||||||
|
|||||||
@@ -1,9 +1,19 @@
|
|||||||
import { execFile } from 'child_process'
|
import { execFile } from 'child_process'
|
||||||
|
import { existsSync } from 'fs'
|
||||||
import { promisify } from 'util'
|
import { promisify } from 'util'
|
||||||
|
|
||||||
const execFileAsync = promisify(execFile)
|
const execFileAsync = promisify(execFile)
|
||||||
|
|
||||||
const execOpts = { windowsHide: true }
|
const execOpts = { windowsHide: true }
|
||||||
|
const isDocker = existsSync('/.dockerenv')
|
||||||
|
|
||||||
|
function resolveHermesBin(): string {
|
||||||
|
const envBin = process.env.HERMES_BIN?.trim()
|
||||||
|
if (envBin) return envBin
|
||||||
|
return 'hermes'
|
||||||
|
}
|
||||||
|
|
||||||
|
const HERMES_BIN = resolveHermesBin()
|
||||||
|
|
||||||
export interface HermesSession {
|
export interface HermesSession {
|
||||||
id: string
|
id: string
|
||||||
@@ -64,7 +74,7 @@ export async function listSessions(source?: string, limit?: number): Promise<Her
|
|||||||
if (source) args.push('--source', source)
|
if (source) args.push('--source', source)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { stdout } = await execFileAsync('hermes', args, {
|
const { stdout } = await execFileAsync(HERMES_BIN, args, {
|
||||||
maxBuffer: 50 * 1024 * 1024, // 50MB
|
maxBuffer: 50 * 1024 * 1024, // 50MB
|
||||||
timeout: 30000,
|
timeout: 30000,
|
||||||
...execOpts,
|
...execOpts,
|
||||||
@@ -128,7 +138,7 @@ export async function getSession(id: string): Promise<HermesSession | null> {
|
|||||||
const args = ['sessions', 'export', '-', '--session-id', id]
|
const args = ['sessions', 'export', '-', '--session-id', id]
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { stdout } = await execFileAsync('hermes', args, {
|
const { stdout } = await execFileAsync(HERMES_BIN, args, {
|
||||||
maxBuffer: 50 * 1024 * 1024,
|
maxBuffer: 50 * 1024 * 1024,
|
||||||
timeout: 30000,
|
timeout: 30000,
|
||||||
...execOpts,
|
...execOpts,
|
||||||
@@ -174,7 +184,7 @@ export async function getSession(id: string): Promise<HermesSession | null> {
|
|||||||
*/
|
*/
|
||||||
export async function deleteSession(id: string): Promise<boolean> {
|
export async function deleteSession(id: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
await execFileAsync('hermes', ['sessions', 'delete', id, '--yes'], {
|
await execFileAsync(HERMES_BIN, ['sessions', 'delete', id, '--yes'], {
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
...execOpts,
|
...execOpts,
|
||||||
})
|
})
|
||||||
@@ -190,7 +200,7 @@ export async function deleteSession(id: string): Promise<boolean> {
|
|||||||
*/
|
*/
|
||||||
export async function renameSession(id: string, title: string): Promise<boolean> {
|
export async function renameSession(id: string, title: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
await execFileAsync('hermes', ['sessions', 'rename', id, title], {
|
await execFileAsync(HERMES_BIN, ['sessions', 'rename', id, title], {
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
...execOpts,
|
...execOpts,
|
||||||
})
|
})
|
||||||
@@ -212,7 +222,7 @@ export interface LogFileInfo {
|
|||||||
*/
|
*/
|
||||||
export async function getVersion(): Promise<string> {
|
export async function getVersion(): Promise<string> {
|
||||||
try {
|
try {
|
||||||
const { stdout } = await execFileAsync('hermes', ['--version'], { timeout: 5000, ...execOpts })
|
const { stdout } = await execFileAsync(HERMES_BIN, ['--version'], { timeout: 5000, ...execOpts })
|
||||||
return stdout.trim()
|
return stdout.trim()
|
||||||
} catch {
|
} catch {
|
||||||
return ''
|
return ''
|
||||||
@@ -223,7 +233,12 @@ export async function getVersion(): Promise<string> {
|
|||||||
* Start Hermes gateway (uses launchd/systemd)
|
* Start Hermes gateway (uses launchd/systemd)
|
||||||
*/
|
*/
|
||||||
export async function startGateway(): Promise<string> {
|
export async function startGateway(): Promise<string> {
|
||||||
const { stdout, stderr } = await execFileAsync('hermes', ['gateway', 'start'], {
|
if (isDocker) {
|
||||||
|
const pid = await startGatewayBackground()
|
||||||
|
return pid ? `Gateway started (PID: ${pid})` : 'Gateway start triggered'
|
||||||
|
}
|
||||||
|
|
||||||
|
const { stdout, stderr } = await execFileAsync(HERMES_BIN, ['gateway', 'start'], {
|
||||||
timeout: 30000,
|
timeout: 30000,
|
||||||
...execOpts,
|
...execOpts,
|
||||||
})
|
})
|
||||||
@@ -236,7 +251,7 @@ export async function startGateway(): Promise<string> {
|
|||||||
*/
|
*/
|
||||||
export async function startGatewayBackground(): Promise<number | null> {
|
export async function startGatewayBackground(): Promise<number | null> {
|
||||||
const { spawn } = require('child_process') as typeof import('child_process')
|
const { spawn } = require('child_process') as typeof import('child_process')
|
||||||
const child = spawn('hermes', ['gateway', 'run'], {
|
const child = spawn(HERMES_BIN, ['gateway', 'run'], {
|
||||||
detached: true,
|
detached: true,
|
||||||
stdio: 'ignore',
|
stdio: 'ignore',
|
||||||
windowsHide: true,
|
windowsHide: true,
|
||||||
@@ -249,7 +264,13 @@ export async function startGatewayBackground(): Promise<number | null> {
|
|||||||
* Restart Hermes gateway
|
* Restart Hermes gateway
|
||||||
*/
|
*/
|
||||||
export async function restartGateway(): Promise<string> {
|
export async function restartGateway(): Promise<string> {
|
||||||
const { stdout, stderr } = await execFileAsync('hermes', ['gateway', 'restart'], {
|
if (isDocker) {
|
||||||
|
try { await stopGateway() } catch { }
|
||||||
|
const pid = await startGatewayBackground()
|
||||||
|
return pid ? `Gateway restarted (PID: ${pid})` : 'Gateway restart triggered'
|
||||||
|
}
|
||||||
|
|
||||||
|
const { stdout, stderr } = await execFileAsync(HERMES_BIN, ['gateway', 'restart'], {
|
||||||
timeout: 30000,
|
timeout: 30000,
|
||||||
...execOpts,
|
...execOpts,
|
||||||
})
|
})
|
||||||
@@ -260,7 +281,7 @@ export async function restartGateway(): Promise<string> {
|
|||||||
* Stop Hermes gateway
|
* Stop Hermes gateway
|
||||||
*/
|
*/
|
||||||
export async function stopGateway(): Promise<string> {
|
export async function stopGateway(): Promise<string> {
|
||||||
const { stdout, stderr } = await execFileAsync('hermes', ['gateway', 'stop'], {
|
const { stdout, stderr } = await execFileAsync(HERMES_BIN, ['gateway', 'stop'], {
|
||||||
timeout: 30000,
|
timeout: 30000,
|
||||||
...execOpts,
|
...execOpts,
|
||||||
})
|
})
|
||||||
@@ -272,7 +293,7 @@ export async function stopGateway(): Promise<string> {
|
|||||||
*/
|
*/
|
||||||
export async function listLogFiles(): Promise<LogFileInfo[]> {
|
export async function listLogFiles(): Promise<LogFileInfo[]> {
|
||||||
try {
|
try {
|
||||||
const { stdout } = await execFileAsync('hermes', ['logs', 'list'], {
|
const { stdout } = await execFileAsync(HERMES_BIN, ['logs', 'list'], {
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
...execOpts,
|
...execOpts,
|
||||||
})
|
})
|
||||||
@@ -311,7 +332,7 @@ export async function readLogs(
|
|||||||
if (since) args.push('--since', since)
|
if (since) args.push('--since', since)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { stdout } = await execFileAsync('hermes', args, {
|
const { stdout } = await execFileAsync(HERMES_BIN, args, {
|
||||||
maxBuffer: 10 * 1024 * 1024,
|
maxBuffer: 10 * 1024 * 1024,
|
||||||
timeout: 15000,
|
timeout: 15000,
|
||||||
...execOpts,
|
...execOpts,
|
||||||
@@ -349,7 +370,7 @@ export interface HermesProfileDetail {
|
|||||||
*/
|
*/
|
||||||
export async function listProfiles(): Promise<HermesProfile[]> {
|
export async function listProfiles(): Promise<HermesProfile[]> {
|
||||||
try {
|
try {
|
||||||
const { stdout } = await execFileAsync('hermes', ['profile', 'list'], {
|
const { stdout } = await execFileAsync(HERMES_BIN, ['profile', 'list'], {
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
...execOpts,
|
...execOpts,
|
||||||
})
|
})
|
||||||
@@ -385,7 +406,7 @@ export async function listProfiles(): Promise<HermesProfile[]> {
|
|||||||
*/
|
*/
|
||||||
export async function getProfile(name: string): Promise<HermesProfileDetail> {
|
export async function getProfile(name: string): Promise<HermesProfileDetail> {
|
||||||
try {
|
try {
|
||||||
const { stdout } = await execFileAsync('hermes', ['profile', 'show', name], {
|
const { stdout } = await execFileAsync(HERMES_BIN, ['profile', 'show', name], {
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
...execOpts,
|
...execOpts,
|
||||||
})
|
})
|
||||||
@@ -429,7 +450,7 @@ export async function createProfile(name: string, clone?: boolean): Promise<stri
|
|||||||
if (clone) args.push('--clone')
|
if (clone) args.push('--clone')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { stdout, stderr } = await execFileAsync('hermes', args, {
|
const { stdout, stderr } = await execFileAsync(HERMES_BIN, args, {
|
||||||
timeout: 15000,
|
timeout: 15000,
|
||||||
...execOpts,
|
...execOpts,
|
||||||
})
|
})
|
||||||
@@ -445,7 +466,7 @@ export async function createProfile(name: string, clone?: boolean): Promise<stri
|
|||||||
*/
|
*/
|
||||||
export async function deleteProfile(name: string): Promise<boolean> {
|
export async function deleteProfile(name: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
await execFileAsync('hermes', ['profile', 'delete', name, '--yes'], {
|
await execFileAsync(HERMES_BIN, ['profile', 'delete', name, '--yes'], {
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
...execOpts,
|
...execOpts,
|
||||||
})
|
})
|
||||||
@@ -461,7 +482,7 @@ export async function deleteProfile(name: string): Promise<boolean> {
|
|||||||
*/
|
*/
|
||||||
export async function renameProfile(oldName: string, newName: string): Promise<boolean> {
|
export async function renameProfile(oldName: string, newName: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
await execFileAsync('hermes', ['profile', 'rename', oldName, newName], {
|
await execFileAsync(HERMES_BIN, ['profile', 'rename', oldName, newName], {
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
...execOpts,
|
...execOpts,
|
||||||
})
|
})
|
||||||
@@ -477,7 +498,7 @@ export async function renameProfile(oldName: string, newName: string): Promise<b
|
|||||||
*/
|
*/
|
||||||
export async function useProfile(name: string): Promise<string> {
|
export async function useProfile(name: string): Promise<string> {
|
||||||
try {
|
try {
|
||||||
const { stdout, stderr } = await execFileAsync('hermes', ['profile', 'use', name], {
|
const { stdout, stderr } = await execFileAsync(HERMES_BIN, ['profile', 'use', name], {
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
...execOpts,
|
...execOpts,
|
||||||
})
|
})
|
||||||
@@ -496,7 +517,7 @@ export async function exportProfile(name: string, outputPath?: string): Promise<
|
|||||||
if (outputPath) args.push('--output', outputPath)
|
if (outputPath) args.push('--output', outputPath)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { stdout, stderr } = await execFileAsync('hermes', args, {
|
const { stdout, stderr } = await execFileAsync(HERMES_BIN, args, {
|
||||||
timeout: 60000,
|
timeout: 60000,
|
||||||
...execOpts,
|
...execOpts,
|
||||||
})
|
})
|
||||||
@@ -512,7 +533,7 @@ export async function exportProfile(name: string, outputPath?: string): Promise<
|
|||||||
*/
|
*/
|
||||||
export async function setupReset(): Promise<string> {
|
export async function setupReset(): Promise<string> {
|
||||||
try {
|
try {
|
||||||
const { stdout, stderr } = await execFileAsync('hermes', ['setup', '--non-interactive', '--reset'], {
|
const { stdout, stderr } = await execFileAsync(HERMES_BIN, ['setup', '--non-interactive', '--reset'], {
|
||||||
timeout: 30000,
|
timeout: 30000,
|
||||||
...execOpts,
|
...execOpts,
|
||||||
})
|
})
|
||||||
@@ -531,7 +552,7 @@ export async function importProfile(archivePath: string, name?: string): Promise
|
|||||||
if (name) args.push('--name', name)
|
if (name) args.push('--name', name)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { stdout, stderr } = await execFileAsync('hermes', args, {
|
const { stdout, stderr } = await execFileAsync(HERMES_BIN, args, {
|
||||||
timeout: 60000,
|
timeout: 60000,
|
||||||
...execOpts,
|
...execOpts,
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user