refactor: DevTools → ethend 重命名 + 加入生产环境

- 目录 devtools/ → ethend/
- CLI 脚本 devtools.sh/.bat → ethend.sh/.bat
- 环境变量 DEVTOOLS_PORT → ETHEND_PORT
- docker-compose.yml 新增 ethend 服务(生产部署)
- 同步更新全部文档、注释和配置文件

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-30 09:32:36 +08:00
parent 27187997b3
commit 365f5ceb2f
30 changed files with 455 additions and 189 deletions
+3
View File
@@ -45,6 +45,9 @@ ENABLE_BACKGROUND_THINKING=true
# ========== Webhook(可选) ==========
WEBHOOK_API_KEY=
# ========== 管理控制台端口 ==========
ETHEND_PORT=9090
# ========== 反向代理端口(避免与已有 nginx 等服务冲突) ==========
CADDY_HTTP_PORT=80
CADDY_HTTPS_PORT=443
+4 -4
View File
@@ -59,10 +59,10 @@ docs/*
debug/cache/
debug/logs/
# ========== DevTools 运行时 ==========
devtools/node_modules/
devtools/logs/
devtools/package-lock.json
# ========== ethend 运行时 ==========
ethend/node_modules/
ethend/logs/
ethend/package-lock.json
# ========== 语音服务外部依赖 (C++ 编译产物 / 模型文件) ==========
backend/voice-service/whisper.cpp/
+32 -32
View File
@@ -1,6 +1,6 @@
# Cyrene 部署指南
三种方式启动开发环境:**DevTools 一键**(推荐)、**手动逐服务**、**Docker Compose**。
三种方式启动开发环境:**ethend 一键**(推荐)、**手动逐服务**、**Docker Compose**。
---
@@ -9,9 +9,9 @@
| 依赖 | 版本 | 用途 |
|------|------|------|
| Go | 1.21+ | 编译后端服务 |
| Node.js | 20+ (LTS) | 前端 / DevTools |
| Node.js | 20+ (LTS) | 前端 / ethend |
| Docker & Docker Compose | — | 数据库 & 基础设施 |
| Git Bash (Windows) | — | 运行 devtools.sh |
| Git Bash (Windows) | — | 运行 ethend.sh |
### Windows 额外要求
@@ -23,14 +23,14 @@ Windows 提供两个启动脚本:
| 脚本 | 终端 | 适用场景 |
|------|------|----------|
| `devtools.bat` | CMD / PowerShell | 双击运行,无需 Git Bash |
| `devtools.sh` | Git Bash | 完整 CLI 体验(推荐) |
| `ethend.bat` | CMD / PowerShell | 双击运行,无需 Git Bash |
| `ethend.sh` | Git Bash | 完整 CLI 体验(推荐) |
两者支持相同的命令集,日常开发推荐使用 Git Bash 运行 `./devtools.sh`;快速启动可直接双击 `devtools.bat`
两者支持相同的命令集,日常开发推荐使用 Git Bash 运行 `./ethend.sh`;快速启动可直接双击 `ethend.bat`
---
## 方式一:DevTools 一键启动(推荐)
## 方式一:ethend 一键启动(推荐)
### 1. 配置环境变量
@@ -44,13 +44,13 @@ cp backend/.env.example backend/.env
### 2. 启动数据库
```bash
./devtools.sh db:start
./ethend.sh db:start
```
### 3. 编译并启动全部服务
```bash
./devtools.sh start --build
./ethend.sh start --build
```
首次运行会编译全部后端 Go 服务(约 1-2 分钟),之后按依赖顺序启动全部服务,每步等待健康检查通过。
@@ -59,10 +59,10 @@ cp backend/.env.example backend/.env
| 地址 | 说明 |
|------|------|
| `http://localhost:9090` | DevTools 管理面板 |
| `http://localhost:9090` | ethend 管理面板 |
| `http://localhost:5173` | 前端聊天界面 |
详细 CLI 用法见 [docs/api/devtools.md](docs/api/devtools.md)。
详细 CLI 用法见 [docs/api/ethend.md](docs/api/ethend.md)。
---
@@ -111,7 +111,7 @@ cd frontend/web && npm install && npx vite --host 0.0.0.0
docker compose -f docker-compose.dev.yml up -d
```
启动服务:postgres, redis, qdrant, minio, searxng, memory-service, voice-service, iot-debug-service, ai-core, gateway, devtools。前端需本地启动。
启动服务:postgres, redis, qdrant, minio, searxng, memory-service, voice-service, iot-debug-service, ai-core, gateway, ethend。前端需本地启动。
### 生产环境
@@ -140,16 +140,16 @@ Cyrene/
│ ├── voice-service/ # 语音服务 (DashScope STT + Edge-TTS)
│ ├── iot-debug-service/ # IoT 调试服务 (8 个模拟智能家居设备)
│ └── pkg/ # 共享包 (logger, plugins — 15 个通用插件/工具)
├── devtools/ # DevTools 管理面板 (Express + WebSocket)
├── ethend/ # ethend 管理面板 (Express + WebSocket)
├── scripts/ # 辅助脚本 (migrate / tunnel / whisper-setup / pg-backup)
├── searxng/ # SearXNG 搜索引擎配置
├── backups/ # 数据库备份文件
├── test/ # E2E 测试
├── docs/ # 文档
├── docker-compose.dev.db.yml # 开发基础设施
├── docker-compose.dev.yml # 开发环境 (DB + 后端 + DevTools + SearXNG)
├── docker-compose.dev.yml # 开发环境 (DB + 后端 + ethend + SearXNG)
├── docker-compose.yml # 生产环境 (+ Caddy)
└── devtools.sh # DevTools CLI
└── ethend.sh # ethend CLI
```
---
@@ -165,7 +165,7 @@ Cyrene/
| 8091 | Memory Service | 否 |
| 8088 | SearXNG | 否 |
| 8093 | Voice Service | 否 |
| 9090 | DevTools | 是 |
| 9090 | ethend | 是 |
| 5432 | PostgreSQL | 否 |
| 6379 | Redis | 否 |
| 6333 | Qdrant HTTP | 否 |
@@ -239,10 +239,10 @@ Cyrene/
## 数据库管理
```bash
# 通过 DevTools CLI
./devtools.sh db:start # 启动
./devtools.sh db:stop # 停止
./devtools.sh db:status # 状态检查
# 通过 ethend CLI
./ethend.sh db:start # 启动
./ethend.sh db:stop # 停止
./ethend.sh db:status # 状态检查
# 直接 Docker Compose
docker compose -f docker-compose.dev.db.yml up -d
@@ -280,23 +280,23 @@ docker exec -it cyrene_postgres psql -U cyrene -d cyrene_ai
| 脚本 | 终端 | 说明 |
|------|------|------|
| `devtools.bat` | CMD / PowerShell | 双击即可运行,无需 Git Bash |
| `devtools.sh` | Git Bash | 完整 CLI(推荐) |
| `ethend.bat` | CMD / PowerShell | 双击即可运行,无需 Git Bash |
| `ethend.sh` | Git Bash | 完整 CLI(推荐) |
```cmd
:: CMD 中直接运行
devtools.bat start --build
devtools.bat status
devtools.bat logs gateway
ethend.bat start --build
ethend.bat status
ethend.bat logs gateway
:: Git Bash 中运行
./devtools.sh start --build
./devtools.sh status
./ethend.sh start --build
./ethend.sh status
```
### 编译差异
Windows 下 Go 编译产物为 `main.exe` 而非 `main`DevTools 已自动处理此差异,手动编译时需要注意:
Windows 下 Go 编译产物为 `main.exe` 而非 `main`ethend 已自动处理此差异,手动编译时需要注意:
```bash
# Windows (Git Bash / PowerShell)
@@ -325,9 +325,9 @@ netstat -ano | findstr ":8080"
# 杀进程 (PowerShell)
powershell -Command "Stop-Process -Id <PID> -Force"
# 或者直接用 DevTools CLI(跨平台兼容)
./devtools.sh status # 查看各服务端口状态
./devtools.sh stop # 停止 DevTools
# 或者直接用 ethend CLI(跨平台兼容)
./ethend.sh status # 查看各服务端口状态
./ethend.sh stop # 停止 ethend
```
### Docker Desktop
@@ -361,7 +361,7 @@ export PATH="$PATH:/c/Program Files/nodejs"
| 症状 | 可能原因 | 解决 |
|------|----------|------|
| `go: command not found` | Go 未加入 PATH | 重启 Git Bash 或手动 `export PATH` |
| `Only one usage of each socket address` | 端口被占用 | `./devtools.sh stop` 或用 PowerShell 杀进程 |
| `Only one usage of each socket address` | 端口被占用 | `./ethend.sh stop` 或用 PowerShell 杀进程 |
| `docker: error during connect` | Docker Desktop 未启动 | 启动 Docker Desktop 等待就绪 |
| `GOWORK` 相关编译错误 | 未设置 GOWORK=off | `export GOWORK=off` 或在命令前加 `GOWORK=off` |
| `npm install` 卡住 | Windows 下 npm 网络问题 | 设置镜像 `npm config set registry https://registry.npmmirror.com` |
+7 -7
View File
@@ -19,7 +19,7 @@
| 前端配置文件 | `frontend/web/vite.config.ts`, `frontend/web/tsconfig.json`, `frontend/web/tailwind.config.ts` 等 | 构建和样式配置 |
| 前端入口 | `frontend/web/index.html` | HTML 入口 |
| 公共资源 | `frontend/web/public/` | 静态资源(头像、背景图、manifest 等) |
| DevTools | `devtools/` | 管理面板源码 |
| ethend | `ethend/` | 管理面板源码 |
| Python 测试脚本 | `debug/cache/*.py` | 调试和端到端测试脚本 |
| Shell 调试脚本 | `debug/*.sh`, `debug/*.mjs` | Chromium 调试、诊断脚本 |
| 项目配置 | `.editorconfig`, `.gitignore`, `package.json` 等 | 项目级配置 |
@@ -36,7 +36,7 @@
|------|----------|------|
| 编译后的 Go 二进制 | `main`, `backend/*/main`, `backend/*/cmd/*` (不含 `.go`) | 平台相关,需在 Windows 重新编译 |
| Windows 可执行文件 | `*.exe` | 旧的 Windows 编译产物 |
| Node.js 依赖 | `node_modules/`, `frontend/web/node_modules/`, `frontend/node_modules/`, `devtools/node_modules/` | 体积大,通过 `npm install` 重新安装 |
| Node.js 依赖 | `node_modules/`, `frontend/web/node_modules/`, `frontend/node_modules/`, `ethend/node_modules/` | 体积大,通过 `npm install` 重新安装 |
| 前端构建产物 | `frontend/web/dist/` | 通过 `npm run build` 重新构建 |
| 敏感配置文件 | `backend/.env` | 包含 API 密钥和密码 |
| 锁文件 | `package-lock.json`, `frontend/web/package-lock.json`, `frontend/package-lock.json` | 跨平台 npm 依赖树可能不同 |
@@ -241,10 +241,10 @@ docker-compose -f docker-compose.dev.db.yml up -d
### 6.5 启动顺序
**推荐使用 DevTools 一键启动**(自动编译 + 按序启动 + 健康检查):
**推荐使用 ethend 一键启动**(自动编译 + 按序启动 + 健康检查):
```cmd
cd devtools
cd ethend
node src\index.js
:: 浏览器打开 http://localhost:9090,点击「一键启动」
```
@@ -252,10 +252,10 @@ node src\index.js
或使用启动脚本:
```cmd
devtools.bat
ethend.bat
```
DevTools 会按以下顺序自动编译并启动所有 6 个服务:
ethend 会按以下顺序自动编译并启动所有 6 个服务:
| 顺序 | 服务 | 端口 | 说明 |
|------|------|------|------|
@@ -266,7 +266,7 @@ DevTools 会按以下顺序自动编译并启动所有 6 个服务:
| 5 | gateway | 8080 | API 网关 / JWT / WebSocket |
| 6 | frontend | 5173 | React 开发服务器 |
> 每个步骤会自动等待健康检查通过后再启动下一个服务。如果 Go 二进制未编译,DevTools 会自动先编译再启动。
> 每个步骤会自动等待健康检查通过后再启动下一个服务。如果 Go 二进制未编译,ethend 会自动先编译再启动。
如需手动逐个启动:
+14 -14
View File
@@ -89,10 +89,10 @@ docker compose -f docker-compose.dev.db.yml up -d
```bash
# Linux / macOS (Git Bash)
./devtools.sh start --build
./ethend.sh start --build
# Windows CMD / PowerShell
devtools.bat start --build
ethend.bat start --build
```
按依赖顺序编译并启动全部 8 个服务:memory → plugin-manager → iot-debug → voice → ai-core → platform-bridge → gateway → frontend。
@@ -102,21 +102,21 @@ devtools.bat start --build
| 地址 | 说明 |
|------|------|
| `http://localhost:5173` | 前端聊天界面 |
| `http://localhost:9090` | DevTools 管理面板 |
| `http://localhost:9090` | ethend 管理面板 |
使用 `.env` 中配置的 `ADMIN_USERNAME` / `ADMIN_PASSWORD` 登录。
### 其他 CLI 命令
```bash
./devtools.sh status # 查看服务状态
./devtools.sh logs gateway # 查看 Gateway 日志
./devtools.sh build ai-core # 单独编译 AI-Core
./devtools.sh db:status # 检查数据库状态
./devtools.sh help # 完整帮助
./ethend.sh status # 查看服务状态
./ethend.sh logs gateway # 查看 Gateway 日志
./ethend.sh build ai-core # 单独编译 AI-Core
./ethend.sh db:status # 检查数据库状态
./ethend.sh help # 完整帮助
```
详见 [docs/api/devtools.md](docs/api/devtools.md)。
详见 [docs/api/ethend.md](docs/api/ethend.md)。
---
@@ -135,7 +135,7 @@ Cyrene/
│ ├── plugin-manager/ # 插件管理器 (管理 API,插件逻辑在 pkg/plugins)
│ ├── platform-bridge/ # 多平台桥接 (QQ / Telegram / Discord / Webhook)
│ └── pkg/ # 共享包 (logger, plugins — 15 个通用插件/工具)
├── devtools/ # DevTools 管理面板 (Express + WebSocket)
├── ethend/ # ethend 管理面板 (Express + WebSocket)
├── scripts/ # 辅助脚本 (migrate / tunnel / whisper-setup / pg-backup)
├── backups/ # 数据库备份文件 (.gitignore)
├── test/ # E2E 测试
@@ -145,8 +145,8 @@ Cyrene/
├── docker-compose.dev.db.yml # 开发基础设施 (仅 DB)
├── docker-compose.dev.yml # 开发环境一键启动
├── docker-compose.yml # 生产环境 (含 Caddy)
├── devtools.sh # DevTools CLI (Git Bash)
├── devtools.bat # DevTools CLI (CMD / PowerShell)
├── ethend.sh # ethend CLI (Git Bash)
├── ethend.bat # ethend CLI (CMD / PowerShell)
└── Caddyfile # 反向代理配置
```
@@ -165,7 +165,7 @@ Cyrene/
| 8093 | Voice Service | 否 |
| 8094 | Plugin Manager | 否 |
| 8095 | Platform Bridge | 否 |
| 9090 | DevTools | 是 |
| 9090 | ethend | 是 |
| 5432 | PostgreSQL | 否 |
| 6379 | Redis | 否 |
| 6333 | Qdrant HTTP | 否 |
@@ -200,7 +200,7 @@ Cyrene/
|------|------|
| [Deploy.md](Deploy.md) | 部署指南(含 Windows 说明) |
| [docs/api/gateway-api.md](docs/api/gateway-api.md) | 客户端 API 文档 |
| [docs/api/devtools.md](docs/api/devtools.md) | DevTools CLI + Web 控制台文档 |
| [docs/api/ethend.md](docs/api/ethend.md) | ethend CLI + Web 控制台文档 |
| [docs/api/backend-services/](docs/api/backend-services/) | 后端服务 API 文档 |
| [docs/dev_must_read.md](docs/dev_must_read.md) | 开发者必读 |
| [docs/pg-backup-migration.md](docs/pg-backup-migration.md) | PG 备份与迁移指南 |
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -176,7 +176,7 @@ func (h *Hub) cleanupIdleSessions() {
}
}
// GetAllActiveSessions 返回所有会话状态(包括 idle),供 DevTools 监看使用
// GetAllActiveSessions 返回所有会话状态(包括 idle),供 ethend 监看使用
func (h *Hub) GetAllActiveSessions() []*SessionState {
h.mu.RLock()
defer h.mu.RUnlock()
+224
View File
@@ -0,0 +1,224 @@
package main
import (
"flag"
"fmt"
"io"
"net/http"
"os"
"os/signal"
"time"
"github.com/gorilla/websocket"
)
func main() {
mode := flag.String("mode", "offline", "测试模式: offline (非实时) 或 realtime (实时)")
file := flag.String("file", "", "音频文件路径 (WAV/MP3/OGG/FLAC)")
server := flag.String("server", "http://localhost:8093", "Voice-Service 地址")
lang := flag.String("lang", "zh", "语言代码")
flag.Parse()
if *file == "" {
fmt.Println("用法: test_asr -mode=offline -file=audio.wav [-server=http://localhost:8093]")
os.Exit(1)
}
switch *mode {
case "offline":
testOffline(*server, *file, *lang)
case "realtime":
testRealtime(*server, *file, *lang)
default:
fmt.Printf("未知模式: %s (支持: offline, realtime)\n", *mode)
os.Exit(1)
}
}
// testOffline 测试非实时语音识别 (HTTP multipart 上传)。
func testOffline(server, filePath, lang string) {
fmt.Printf("=== 非实时 ASR 测试 ===\n")
fmt.Printf("服务器: %s\n", server)
fmt.Printf("文件: %s\n", filePath)
fmt.Printf("语言: %s\n\n", lang)
// 读取音频文件
audioData, err := os.ReadFile(filePath)
if err != nil {
fmt.Printf("读取文件失败: %v\n", err)
os.Exit(1)
}
fmt.Printf("音频大小: %d bytes\n", len(audioData))
// 创建 multipart 请求
req, err := http.NewRequest("POST", server+"/api/v1/transcribe", nil)
if err != nil {
fmt.Printf("创建请求失败: %v\n", err)
os.Exit(1)
}
// 使用 multipart form
body, contentType, err := createMultipartBody(audioData, filePath, lang)
if err != nil {
fmt.Printf("创建 multipart body 失败: %v\n", err)
os.Exit(1)
}
req.Body = body
req.Header.Set("Content-Type", contentType)
start := time.Now()
resp, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Printf("请求失败: %v\n", err)
os.Exit(1)
}
defer resp.Body.Close()
elapsed := time.Since(start)
respBody, _ := io.ReadAll(resp.Body)
fmt.Printf("状态码: %d\n", resp.StatusCode)
fmt.Printf("耗时: %v\n", elapsed)
fmt.Printf("响应:\n%s\n", string(respBody))
if resp.StatusCode == 200 {
fmt.Println("\n✅ 非实时语音识别成功!")
} else {
fmt.Println("\n❌ 非实时语音识别失败")
}
}
// testRealtime 测试实时语音识别 (WebSocket 流式)。
func testRealtime(server, filePath, lang string) {
fmt.Printf("=== 实时 ASR 测试 ===\n")
fmt.Printf("服务器: %s\n", server)
fmt.Printf("文件: %s\n", filePath)
fmt.Printf("语言: %s\n\n", lang)
// 读取音频文件
audioData, err := os.ReadFile(filePath)
if err != nil {
fmt.Printf("读取文件失败: %v\n", err)
os.Exit(1)
}
fmt.Printf("音频大小: %d bytes\n", len(audioData))
// 推断格式
format := inferFormat(filePath)
// 连接 WebSocket
wsURL := fmt.Sprintf("ws://%s/api/v1/stt/stream?format=%s&language=%s",
server[7:], format, lang) // 去掉 http:// 前缀
conn, _, err := websocket.DefaultDialer.Dial(wsURL, nil)
if err != nil {
fmt.Printf("WebSocket 连接失败: %v\n", err)
os.Exit(1)
}
defer conn.Close()
fmt.Printf("WebSocket 已连接: %s\n", wsURL)
// 设置 interrupt 处理
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
// goroutine: 读取识别结果
done := make(chan struct{})
go func() {
defer close(done)
for {
_, msg, err := conn.ReadMessage()
if err != nil {
fmt.Printf("读取结果错误: %v\n", err)
return
}
fmt.Printf("◀ 结果: %s\n", string(msg))
}
}()
// 模拟实时流式发送音频(每 100ms 发送 3200 bytes
chunkSize := 3200
totalSent := 0
start := time.Now()
var elapsed time.Duration
cancelled := false
for i := 0; i < len(audioData); i += chunkSize {
end := i + chunkSize
if end > len(audioData) {
end = len(audioData)
}
select {
case <-interrupt:
fmt.Println("\n用户中断")
cancelled = true
default:
}
if cancelled {
break
}
if err := conn.WriteMessage(websocket.BinaryMessage, audioData[i:end]); err != nil {
fmt.Printf("发送音频失败: %v\n", err)
break
}
totalSent += end - i
fmt.Printf("▶ 发送 %d/%d bytes (%.1f%%)\n", totalSent, len(audioData),
float64(totalSent)/float64(len(audioData))*100)
time.Sleep(100 * time.Millisecond)
}
elapsed = time.Since(start)
// 发送停止消息
conn.WriteMessage(websocket.TextMessage, []byte(`{"action":"stop"}`))
// 等待最后的结果
time.Sleep(2 * time.Second)
fmt.Printf("\n总耗时: %v, 总发送: %d bytes\n", elapsed, totalSent)
fmt.Println("✅ 实时语音识别测试完成")
}
func inferFormat(filename string) string {
ext := ""
for i := len(filename) - 1; i >= 0; i-- {
if filename[i] == '.' {
ext = filename[i+1:]
break
}
}
switch ext {
case "wav", "wave":
return "wav"
case "mp3", "mpeg":
return "mp3"
case "ogg", "opus":
return "ogg"
case "flac":
return "flac"
case "m4a", "mp4", "aac":
return "m4a"
default:
return "pcm"
}
}
func createMultipartBody(audioData []byte, filename, lang string) (io.ReadCloser, string, error) {
boundary := "cyrene-asr-test-boundary"
header := fmt.Sprintf("--%s\r\nContent-Disposition: form-data; name=\"audio\"; filename=\"%s\"\r\nContent-Type: application/octet-stream\r\n\r\n",
boundary, filename)
footer := fmt.Sprintf("\r\n--%s\r\nContent-Disposition: form-data; name=\"language\"\r\n\r\n%s\r\n--%s--\r\n",
boundary, lang, boundary)
pr, pw := io.Pipe()
go func() {
pw.Write([]byte(header))
pw.Write(audioData)
pw.Write([]byte(footer))
pw.Close()
}()
return pr, "multipart/form-data; boundary=" + boundary, nil
}
+1 -1
View File
@@ -668,7 +668,7 @@ print("\n" + "=" * 70)
print("Part I: 服务日志摘要")
print("=" * 70)
LOG_DIR = "/home/aska/Code/Cyrene/devtools/logs"
LOG_DIR = "/home/aska/Code/Cyrene/ethend/logs"
log_files = {
"ai-core": f"{LOG_DIR}/ai-core.log",
"gateway": f"{LOG_DIR}/gateway.log",
+1 -1
View File
@@ -4,7 +4,7 @@ import json, time, base64, os
from websocket import create_connection
PAGE_URL = "http://localhost:5199/"
CDP_WS = "ws://127.0.0.1:9225/devtools/browser/b2fca0da-35d6-4180-8413-eddf53753c6a"
CDP_WS = "ws://127.0.0.1:9225/ethend/browser/b2fca0da-35d6-4180-8413-eddf53753c6a"
def send_cmd(ws, method, params=None, msg_id=1):
payload = json.dumps({"id": msg_id, "method": method, "params": params or {}})
+6 -6
View File
@@ -74,19 +74,19 @@ services:
- "8088:8080"
restart: unless-stopped
# ========== 开发调试工具 ==========
devtools:
container_name: cyrene_devtools
# ==========管理控制台 ==========
ethend:
container_name: cyrene_ethend
build:
context: ./devtools
context: ./ethend
dockerfile: Dockerfile
environment:
DEVTOOLS_PORT: "9090"
ETHEND_PORT: "9090"
GATEWAY_URL: http://gateway:8080
AI_CORE_URL: http://ai-core:8081
MEMORY_SERVICE_URL: http://memory-service:8091
VOICE_SERVICE_URL: http://voice-service:8093
PLATFORM_BRIDGE_URL: http://platform-bridge:8095
IOT_DEBUG_SERVICE_URL: http://iot-debug-service:8083
ADMIN_USERNAME: admin
ADMIN_PASSWORD: cyrene-dev-admin
ports:
+21
View File
@@ -143,6 +143,27 @@ services:
IOT_DEBUG_PORT: "8083"
restart: unless-stopped
# ========== 管理控制台 ==========
ethend:
container_name: cyrene_ethend
build:
context: ./ethend
dockerfile: Dockerfile
environment:
ETHEND_PORT: "${ETHEND_PORT:-9090}"
GATEWAY_URL: http://gateway:8080
AI_CORE_URL: http://ai-core:8081
MEMORY_SERVICE_URL: http://memory-service:8091
VOICE_SERVICE_URL: http://voice-service:8093
IOT_DEBUG_SERVICE_URL: http://iot-debug-service:8083
ADMIN_USERNAME: ${ADMIN_USERNAME:-admin}
ADMIN_PASSWORD: ${ADMIN_PASSWORD}
ports:
- "${ETHEND_PORT:-9090}:9090"
depends_on:
- gateway
restart: unless-stopped
# ========== 基础设施 ==========
postgres:
container_name: cyrene_postgres
+17 -17
View File
@@ -1,12 +1,12 @@
# Cyrene DevTools 文档
# Cyrene ethend 文档
DevTools 是 Cyrene 项目的开发管理控制台,提供 Web UI + CLI 两种使用方式。默认端口 **9090**
ethend 是 Cyrene 项目的开发管理控制台,提供 Web UI + CLI 两种使用方式。默认端口 **9090**
---
## 目录
1. [CLI 命令 (devtools.sh)](#1-cli-命令)
1. [CLI 命令 (ethend.sh)](#1-cli-命令)
2. [Web 控制台面板](#2-web-控制台面板)
3. [REST API](#3-rest-api)
@@ -14,16 +14,16 @@ DevTools 是 Cyrene 项目的开发管理控制台,提供 Web UI + CLI 两种
## 1. CLI 命令
`./devtools.sh [命令] [选项]`
`./ethend.sh [命令] [选项]`
### 基本命令
| 命令 | 说明 |
|------|------|
| `start` | 启动 DevTools 控制台 |
| `start` | 启动 ethend 控制台 |
| `start --build` | 编译全部后端服务后启动 |
| `start --fresh` | 强制重启全部后端服务后启动 |
| `stop` | 停止 DevTools 控制台 |
| `stop` | 停止 ethend 控制台 |
| `status` | 查看所有服务状态(运行/停止、PID、uptime) |
| `logs <服务ID> [行数]` | 查看服务日志 |
| `build [服务ID]` | 编译服务(不指定则编译全部) |
@@ -36,7 +36,7 @@ DevTools 是 Cyrene 项目的开发管理控制台,提供 Web UI + CLI 两种
| 选项 | 说明 |
|------|------|
| `--port, -p <端口>` | 指定 DevTools 端口 (默认: 9090) |
| `--port, -p <端口>` | 指定 ethend 端口 (默认: 9090) |
| `--fresh` | 启动前强制重启全部后端服务 |
| `--build` | 启动前编译全部服务 |
@@ -47,12 +47,12 @@ DevTools 是 Cyrene 项目的开发管理控制台,提供 Web UI + CLI 两种
### 示例
```bash
./devtools.sh # 快速启动
./devtools.sh start --build # 编译后启动
./devtools.sh start --fresh # 全新重启
./devtools.sh logs gateway 20 # 查看 Gateway 最近 20 行日志
./devtools.sh build ai-core # 仅编译 AI-Core
./devtools.sh db:status # 检查数据库状态
./ethend.sh # 快速启动
./ethend.sh start --build # 编译后启动
./ethend.sh start --fresh # 全新重启
./ethend.sh logs gateway 20 # 查看 Gateway 最近 20 行日志
./ethend.sh build ai-core # 仅编译 AI-Core
./ethend.sh db:status # 检查数据库状态
```
---
@@ -139,13 +139,13 @@ STT 转录日志(内存环形缓冲,最多 200 条),支持上传音频
## 3. REST API
DevTools 自身暴露的 REST API,供脚本和外部工具使用。
ethend 自身暴露的 REST API,供脚本和外部工具使用。
### 健康检查
```
GET /api/health
→ { "status": "ok", "service": "cyrene-devtools", "uptime": 3600, "wsClients": 2 }
→ { "status": "ok", "service": "cyrene-ethend", "uptime": 3600, "wsClients": 2 }
```
### 服务管理
@@ -191,7 +191,7 @@ GET /api/dashboard
所有代理路由自动获取 Gateway JWT Token(通过 admin 凭据登录并缓存)。
| DevTools 路径 | 目标服务 | 实际路径 |
| ethend 路径 | 目标服务 | 实际路径 |
|---------------|----------|----------|
| `/api/memory/*` | Gateway | `/api/v1/memory/*` |
| `/api/sessions*` | Gateway | `/api/v1/admin/sessions*` |
@@ -201,7 +201,7 @@ GET /api/dashboard
| `/api/tool-calls*` | AI-Core | `/api/v1/tools/calls*` |
| `/api/voice/status` | Voice-Service | `/api/v1/status` |
| `/api/voice/transcribe` | Voice-Service | `/api/v1/transcribe` |
| `/api/voice/logs` | DevTools 内部 | 内存环形缓冲区 |
| `/api/voice/logs` | ethend 内部 | 内存环形缓冲区 |
| `/api/v1/thinking*` | Memory-Service | `/api/v1/thinking*` |
| `/api/memory-timeline` | Memory-Service | 合并 Memories + Thinking |
| `/api/chat-platforms/*` | Platform-Bridge | `/api/v1/*` |
+8 -8
View File
@@ -25,7 +25,7 @@
- [自定义 Hooks](#44-自定义-hooks)
- [API 模块](#45-api-模块)
- [WebSocket 消息流](#46-websocket-消息流)
5. [DevTools (9090) — 调试工具](#五devtools-9090--调试工具)
5. [ethend (9090) — 调试工具](#五ethend-9090--调试工具)
6. [对话管线详解](#六对话管线详解)
7. [数据库设计](#七数据库设计)
8. [安全性](#八安全性)
@@ -588,9 +588,9 @@ App
---
## 五、DevTools (:9090) — 调试工具
## 五、ethend (:9090) — 调试工具
**目录**`devtools/`
**目录**`ethend/`
**类型**Node.js Express 应用
### 5.1 服务管理
@@ -616,14 +616,14 @@ App
### 5.3 关键配置
**构建命令**[config.js](devtools/src/config.js)):
**构建命令**[config.js](ethend/src/config.js)):
```javascript
buildCommand: 'go',
buildArgs: ['build', '-o', isWin ? 'main.exe' : 'main', './cmd/main.go'],
goBin: GO_BIN
```
所有 Go 服务统一编译为 `main.exe`Windows),DevTools 通过 `./main` 启动。**自定义二进制名称不会被 DevTools 识别。**
所有 Go 服务统一编译为 `main.exe`Windows),ethend 通过 `./main` 启动。**自定义二进制名称不会被 ethend 识别。**
---
@@ -767,7 +767,7 @@ postgres://cyrene:cyrene_pass@localhost:5432/cyrene_ai?sslmode=disable
| Memory | 8091 | net/http + pgvector |
| Voice | 8093 | net/http + whisper.cpp |
| Frontend (dev) | 5173 | Vite |
| DevTools | 9090 | Express |
| ethend | 9090 | Express |
### 常用命令
@@ -777,7 +777,7 @@ cd backend/ai-core && GOWORK=off go build -o main.exe ./cmd/main.go
cd backend/gateway && GOWORK=off go build -o main.exe ./cmd/main.go
# ... 其他服务同理
# 或通过 DevTools API
# 或通过 ethend API
curl -X POST http://localhost:9090/api/services/ai-core/build
curl -X POST http://localhost:9090/api/services/gateway/build
@@ -845,7 +845,7 @@ Phase 6 新增,位于 `backend/models.json``.gitignore` 已排除)。格
}
```
**配置管理 API**Gateway adminDevTools 代理提供 UI):
**配置管理 API**Gateway adminethend 代理提供 UI):
| Method | Path | Description |
|--------|------|-------------|
+1
View File
@@ -26,6 +26,7 @@ docker compose logs -f
| memory-service | 8091 (内部) | 记忆检索 |
| voice-service | 8093 (内部) | 语音识别 |
| iot-debug-service | 8083 (内部) | IoT 调试 |
| ethend | 9090 (外部) | 管理控制台 |
| postgres | 5432 (内部) | 数据库 (pgvector/pg16) |
| redis | 6379 (内部) | 缓存 |
| qdrant | 6333 (内部) | 向量数据库 |
+1 -1
View File
@@ -85,7 +85,7 @@ HOST_EXEC_MAX_TIMEOUT=300
## 验证
DevTools 或 API 测试:
ethend 或 API 测试:
```bash
# 通过 API 发送消息,让昔涟使用 os_exec
+28 -28
View File
@@ -1,15 +1,15 @@
@echo off
setlocal enabledelayedexpansion
:: ========================================
:: Cyrene DevTools CLI (Windows CMD)
:: Usage: devtools.bat [command] [options]
:: Cyrene ethend CLI (Windows CMD)
:: Usage: ethend.bat [command] [options]
:: ========================================
set "SCRIPT_DIR=%~dp0"
set "DEVTOOLS_DIR=%SCRIPT_DIR%devtools"
set "ETHEND_DIR=%SCRIPT_DIR%ethend"
set "ROOT=%SCRIPT_DIR%"
if "%DEVTOOLS_PORT%"=="" (set PORT=9090) else (set PORT=%DEVTOOLS_PORT%)
set "LOG_DIR=%DEVTOOLS_DIR%\logs"
if "%ETHEND_PORT%"=="" (set PORT=9090) else (set PORT=%ETHEND_PORT%)
set "LOG_DIR=%ETHEND_DIR%\logs"
set "LOG_FILE=%LOG_DIR%\sh.log"
set "DB_COMPOSE_FILE=%ROOT%docker-compose.dev.db.yml"
@@ -34,15 +34,15 @@ goto :help
:: ==========================================
:help
echo Cyrene DevTools - Development Management Tool
echo Cyrene ethend - Development Management Tool
echo.
echo Usage: devtools.bat [command] [options]
echo Usage: ethend.bat [command] [options]
echo.
echo Commands:
echo start Start DevTools console (default)
echo start Start ethend console (default)
echo start --build Build all services before start
echo start --fresh Force restart all services before start
echo stop Stop DevTools console
echo stop Stop ethend console
echo status Show all service status
echo logs [service] View service logs (default: last 20 lines)
echo build [service] Build service(s) (omit to build all)
@@ -56,12 +56,12 @@ echo --build Build all backend services before start
echo --fresh Force restart all services before start
echo.
echo Examples:
echo devtools.bat Quick start
echo devtools.bat start --build Build and start
echo devtools.bat start --fresh Fresh restart
echo devtools.bat logs gateway View Gateway log
echo devtools.bat build ai-core Build AI-Core only
echo devtools.bat db:status Check database
echo ethend.bat Quick start
echo ethend.bat start --build Build and start
echo ethend.bat start --fresh Fresh restart
echo ethend.bat logs gateway View Gateway log
echo ethend.bat build ai-core Build AI-Core only
echo ethend.bat db:status Check database
echo.
echo Web console: http://localhost:%PORT%
exit /b 0
@@ -85,7 +85,7 @@ exit /b 1
:start
echo.
echo ==========================================
echo Cyrene DevTools
echo Cyrene ethend
echo ==========================================
call :check_node
for /f "tokens=*" %%i in ('!NODE_CMD! --version') do echo Node.js: %%i
@@ -117,7 +117,7 @@ if %DO_FRESH%==1 (
curl -s -o nul "http://localhost:%PORT%/api/health" 2>nul
if %ERRORLEVEL%==0 (
echo.
echo [OK] DevTools already running: http://localhost:%PORT%
echo [OK] ethend already running: http://localhost:%PORT%
echo API: http://localhost:%PORT%/api/health
exit /b 0
)
@@ -129,7 +129,7 @@ for /f "tokens=5" %%a in ('netstat -ano ^| findstr /R ":%PORT% " ^| findstr "LIS
timeout /t 1 /nobreak >nul
)
cd /d "%DEVTOOLS_DIR%"
cd /d "%ETHEND_DIR%"
:: Install dependencies
if not exist "node_modules\" (
@@ -141,16 +141,16 @@ if not exist "node_modules\" (
if not exist "%LOG_DIR%" mkdir "%LOG_DIR%"
echo.
echo [INFO] Starting DevTools on port %PORT%
echo [INFO] Starting ethend on port %PORT%
echo Web UI: http://localhost:%PORT%
echo API: http://localhost:%PORT%/api/health
echo WebSocket: ws://localhost:%PORT%/ws
echo.
start "Cyrene-DevTools" /B !NODE_CMD! src\index.js 1>>"%LOG_FILE%" 2>&1
start "Cyrene-ethend" /B !NODE_CMD! src\index.js 1>>"%LOG_FILE%" 2>&1
:: Health check (up to 30s)
echo [INFO] Waiting for DevTools to be ready...
echo [INFO] Waiting for ethend to be ready...
set MAX_WAIT=30
set WAITED=0
:health_loop
@@ -159,7 +159,7 @@ powershell -Command "try { (Invoke-WebRequest 'http://localhost:%PORT%/api/healt
if %ERRORLEVEL%==0 (
echo.
echo ==========================================
echo DevTools is ready!
echo ethend is ready!
echo Console: http://localhost:%PORT%
echo Log: %LOG_FILE%
echo ==========================================
@@ -179,22 +179,22 @@ exit /b 0
:: ==========================================
:stop
for /f "tokens=5" %%a in ('netstat -ano ^| findstr /R ":%PORT% " ^| findstr "LISTENING"') do (
echo [INFO] Stopping DevTools (PID: %%a)...
echo [INFO] Stopping ethend (PID: %%a)...
taskkill /PID %%a /F >nul 2>&1
echo [OK] DevTools stopped
echo [OK] ethend stopped
exit /b 0
)
echo [INFO] DevTools is not running
echo [INFO] ethend is not running
exit /b 0
:: ==========================================
:status
curl -s -o nul "http://localhost:%PORT%/api/health" 2>nul
if %ERRORLEVEL% neq 0 (
echo [ERROR] DevTools is offline
echo [ERROR] ethend is offline
exit /b 1
)
echo [OK] DevTools online
echo [OK] ethend online
echo.
echo Service Status:
curl -s "http://localhost:%PORT%/api/services" 2>nul | !NODE_CMD! -e "const d=require('fs').readFileSync(0,'utf-8');const s=JSON.parse(d);for(const[k,v]of Object.entries(s)){const icon=v.status==='running'?'+':' ';const pid=v.pid?' (PID:'+v.pid+')':'';const upt=v.uptime?' uptime:'+Math.round(v.uptime/1000)+'s':'';console.log(' ['+icon+'] '+v.name.padEnd(18)+v.status+pid+upt);}" 2>nul
@@ -208,7 +208,7 @@ exit /b 0
set SVC_ID=%2
set LINES=%3
if "%SVC_ID%"=="" (
echo Usage: devtools.bat logs ^<service-id^> [lines]
echo Usage: ethend.bat logs ^<service-id^> [lines]
echo Available: gateway, ai-core, memory-service, tool-engine, voice-service, iot-debug-service, plugin-manager, platform-bridge, frontend
exit /b 1
)
+37 -37
View File
@@ -1,15 +1,15 @@
#!/bin/bash
# ========================================
# Cyrene DevTools 启动脚本
# 管理开发环境: 数据库 / 服务编译 / DevTools 控制台
# Cyrene ethend 启动脚本
# 管理开发环境: 数据库 / 服务编译 / ethend 控制台
# ========================================
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
DEVTOOLS_DIR="$SCRIPT_DIR/devtools"
ETHEND_DIR="$SCRIPT_DIR/ethend"
ROOT="$SCRIPT_DIR"
PORT="${DEVTOOLS_PORT:-9090}"
LOG_DIR="$DEVTOOLS_DIR/logs"
PORT="${ETHEND_PORT:-9090}"
LOG_DIR="$ETHEND_DIR/logs"
LOG_FILE="$LOG_DIR/sh.log"
# 颜色
@@ -28,16 +28,16 @@ esac
# ========== 帮助 ==========
show_help() {
echo -e "${CYAN}Cyrene DevTools${NC} — 开发环境管理工具"
echo -e "${CYAN}Cyrene ethend${NC} — 开发环境管理工具"
echo ""
echo -e "${BOLD}用法:${NC} ./devtools.sh [命令] [选项]"
echo -e "${BOLD}用法:${NC} ./ethend.sh [命令] [选项]"
echo ""
echo -e "${BOLD}命令:${NC}"
echo " (无参数) 启动 DevTools 控制台 (默认)"
echo " start 启动 DevTools 控制台"
echo " (无参数) 启动 ethend 控制台 (默认)"
echo " start 启动 ethend 控制台"
echo " start --fresh 强制重启全部后端服务后启动"
echo " start --build 编译全部服务后启动"
echo " stop 停止 DevTools 控制台"
echo " stop 停止 ethend 控制台"
echo " status 查看所有服务状态"
echo " logs [服务ID] 查看服务日志 (默认显示最近 20 行)"
echo " build [服务ID] 编译服务 (不指定则编译全部)"
@@ -47,17 +47,17 @@ show_help() {
echo " help 显示此帮助"
echo ""
echo -e "${BOLD}选项:${NC}"
echo " --port, -p <端口> 指定 DevTools 端口 (默认: 9090)"
echo " --port, -p <端口> 指定 ethend 端口 (默认: 9090)"
echo " --fresh 启动前强制重启全部后端服务"
echo " --build 启动前编译全部服务"
echo ""
echo -e "${BOLD}示例:${NC}"
echo " ./devtools.sh # 快速启动"
echo " ./devtools.sh start --build # 编译后启动"
echo " ./devtools.sh start --fresh # 全新重启"
echo " ./devtools.sh logs gateway # 查看 Gateway 日志"
echo " ./devtools.sh build ai-core # 仅编译 AI-Core"
echo " ./devtools.sh db:status # 检查数据库"
echo " ./ethend.sh # 快速启动"
echo " ./ethend.sh start --build # 编译后启动"
echo " ./ethend.sh start --fresh # 全新重启"
echo " ./ethend.sh logs gateway # 查看 Gateway 日志"
echo " ./ethend.sh build ai-core # 仅编译 AI-Core"
echo " ./ethend.sh db:status # 检查数据库"
echo ""
echo -e "${BOLD}Web 控制台:${NC} http://localhost:$PORT"
}
@@ -248,8 +248,8 @@ show_logs() {
tail -n "$lines" "$log_path"
}
# ========== 启动 DevTools ==========
start_devtools() {
# ========== 启动 ethend ==========
start_ethend() {
local do_build=false
local do_fresh=false
@@ -265,7 +265,7 @@ start_devtools() {
echo ""
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${CYAN} Cyrene DevTools${NC}"
echo -e "${CYAN} Cyrene ethend${NC}"
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
# 依赖检查
@@ -291,9 +291,9 @@ start_devtools() {
# 检查并释放端口
if port_in_use "$PORT"; then
# 检查是否是已有 DevTools 实例
# 检查是否是已有 ethend 实例
if curl -s -o /dev/null "http://localhost:$PORT/api/health" 2>/dev/null; then
echo -e "${GREEN}DevTools 已在运行: http://localhost:$PORT${NC}"
echo -e "${GREEN}ethend 已在运行: http://localhost:$PORT${NC}"
echo -e "${CYAN} API: http://localhost:$PORT/api/health${NC}"
return 0
fi
@@ -301,8 +301,8 @@ start_devtools() {
kill_port "$PORT"
fi
# 切换到 devtools 目录
cd "$DEVTOOLS_DIR"
# 切换到 ethend 目录
cd "$ETHEND_DIR"
# 安装依赖
if [ ! -d "node_modules" ] || [ ! -f "node_modules/.package-lock.json" ]; then
@@ -314,14 +314,14 @@ start_devtools() {
mkdir -p "$LOG_DIR"
echo ""
echo -e "${GREEN}启动 DevTools 服务器 (端口: $PORT)...${NC}"
echo -e "${GREEN}启动 ethend 服务器 (端口: $PORT)...${NC}"
echo -e "${CYAN} Web 控制台: http://localhost:$PORT${NC}"
echo -e "${CYAN} API: http://localhost:$PORT/api/health${NC}"
echo -e "${CYAN} WebSocket: ws://localhost:$PORT/ws${NC}"
echo ""
echo -e "${YELLOW}正在后台启动...${NC}"
# 后台启动 DevTools
# 后台启动 ethend
nohup node src/index.js > "$LOG_FILE" 2>&1 &
local pid=$!
cd "$ROOT"
@@ -331,7 +331,7 @@ start_devtools() {
if health_check "http://localhost:$PORT/api/health" 30; then
echo ""
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${GREEN} DevTools 已启动!${NC}"
echo -e "${GREEN} ethend 已启动!${NC}"
echo -e "${GREEN} PID: ${pid}${NC}"
echo -e "${GREEN} 控制台: http://localhost:$PORT${NC}"
echo -e "${GREEN} 日志: ${LOG_FILE}${NC}"
@@ -356,25 +356,25 @@ start_devtools() {
fi
}
# ========== 停止 DevTools ==========
stop_devtools() {
# ========== 停止 ethend ==========
stop_ethend() {
if port_in_use "$PORT"; then
echo -e "${CYAN}停止 DevTools...${NC}"
echo -e "${CYAN}停止 ethend...${NC}"
if $IS_WIN; then
kill_port "$PORT"
else
fuser -k "$PORT/tcp" 2>/dev/null || true
fi
echo -e "${GREEN}DevTools 已停止${NC}"
echo -e "${GREEN}ethend 已停止${NC}"
else
echo -e "${YELLOW}DevTools 未在运行${NC}"
echo -e "${YELLOW}ethend 未在运行${NC}"
fi
}
# ========== 查看状态 ==========
show_status() {
if curl -s -o /dev/null "http://localhost:$PORT/api/health" 2>/dev/null; then
echo -e "${GREEN}DevTools 在线${NC}"
echo -e "${GREEN}ethend 在线${NC}"
# 获取服务状态
local status_json=$(curl -s "http://localhost:$PORT/api/services" 2>/dev/null)
@@ -401,7 +401,7 @@ show_status() {
echo ""
db_status
else
echo -e "${RED}DevTools 离线${NC}"
echo -e "${RED}ethend 离线${NC}"
fi
}
@@ -414,17 +414,17 @@ case "$CMD" in
show_help
;;
start|"")
start_devtools "$@"
start_ethend "$@"
;;
stop)
stop_devtools
stop_ethend
;;
status)
show_status
;;
logs)
if [ -z "$1" ]; then
echo -e "${YELLOW}用法: ./devtools.sh logs <服务ID>${NC}"
echo -e "${YELLOW}用法: ./ethend.sh logs <服务ID>${NC}"
echo "可用服务: gateway, ai-core, memory-service, tool-engine, voice-service, iot-debug-service, plugin-manager, platform-bridge, frontend"
exit 1
fi
+2 -2
View File
@@ -8,8 +8,8 @@ RUN npm ci --omit=dev
COPY src/ ./src/
COPY public/ ./public/
ENV DEVTOOLS_PORT=9090
ENV NODE_ENV=development
ENV ETHEND_PORT=9090
ENV NODE_ENV=production
EXPOSE 9090
@@ -1,7 +1,7 @@
{
"name": "cyrene-devtools",
"name": "cyrene-ethend",
"version": "1.0.0",
"description": "Cyrene AI 开发调试工具 - 服务管理、日志监控、性能分析",
"description": "Cyrene AI 高级管理控制台 - 服务管理、日志监控、性能分析",
"private": true,
"type": "module",
"scripts": {
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cyrene DevTools</title>
<title>Cyrene ethend</title>
<style>
/* ========== CSS Variables (深色主题) ========== */
:root {
@@ -747,7 +747,7 @@ input[type="range"] { accent-color: var(--accent); padding: 0; }
<!-- ========== 侧边栏 ========== -->
<aside id="sidebar">
<div class="sidebar-header">
<span class="sidebar-title">🛠️ DevTools</span>
<span class="sidebar-title">ethend</span>
<button id="toggle-sidebar" title="折叠侧边栏"></button>
</div>
<nav class="sidebar-nav">
@@ -1319,7 +1319,7 @@ async function renderDashboard() {
'</div>' +
'<div class="stat-card orange">' +
'<div class="stat-value" id="stat-heap">' + (data.system?.heapUsedMB ?? '—') + ' MB</div>' +
'<div class="stat-label">DevTools 内存</div>' +
'<div class="stat-label">ethend 内存</div>' +
'</div>' +
'</div>' +
@@ -1331,7 +1331,7 @@ async function renderDashboard() {
'<button class="btn btn-sm btn-accent" onclick="svcAction(\'start-all\')">▶ 一键启动</button>' +
'<button class="btn btn-sm" onclick="svcAction(\'start-all-fresh\')">🔄 强制重启全部</button>' +
'<button class="btn btn-sm btn-red" onclick="svcAction(\'stop-all\')">⏹ 全部停止</button>' +
'<button class="btn btn-sm" onclick="restartDevTools()" style="margin-left:8px;border-color:var(--accent);color:var(--accent)" title="重启 DevTools 自身以应用更新">🔁 重启 DevTools</button>' +
'<button class="btn btn-sm" onclick="restartEthend()" style="margin-left:8px;border-color:var(--accent);color:var(--accent)" title="重启 ethend 自身以应用更新">🔁 重启 ethend</button>' +
'</div>' +
'</div>' +
'<div class="cards-grid cards-4" id="dashboard-svc-cards"></div>' +
@@ -2624,11 +2624,11 @@ async function clearSvcLogs() {
renderServiceLog();
}
// ---- DevTools 自重启 ----
async function restartDevTools() {
if (!confirm('确定要重启 DevTools 吗?\n\n页面将在几秒后自动刷新。')) return;
// ---- ethend 自重启 ----
async function restartEthend() {
if (!confirm('确定要重启 ethend 吗?\n\n页面将在几秒后自动刷新。')) return;
try {
await api('/api/devtools/restart', { method: 'POST' });
await api('/api/ethend/restart', { method: 'POST' });
} catch (e) {
// 请求可能在收到响应前就中断(因为服务已退出),这是正常的
}
@@ -1,5 +1,5 @@
/**
* 调试工具配置
* 管理控制台配置
* 定义各服务的启动参数端口健康检查等
*/
@@ -15,7 +15,7 @@ const ROOT = path.resolve(__dirname, '../..');
const isWin = os.platform() === 'win32';
// 读取 backend/.env 文件,将值合并到 process.env(不覆盖已有的环境变量)
// 这样 DevTools 启动各服务时能传递用户配置的凭据
// 这样 ethend 启动各服务时能传递用户配置的凭据
function loadEnvFile() {
const envPath = path.join(ROOT, 'backend', '.env');
try {
@@ -53,7 +53,7 @@ function findGoBin() {
const GO_BIN = findGoBin();
export const DEVTOOLS_PORT = process.env.DEVTOOLS_PORT || 9090;
export const ETHEND_PORT = process.env.ETHEND_PORT || 9090;
export const LOGS_DIR = path.resolve(__dirname, '../logs');
export const GATEWAY_URL = process.env.GATEWAY_URL || 'http://localhost:8080';
export const PLUGIN_MANAGER_URL = process.env.PLUGIN_MANAGER_URL || 'http://localhost:8094';
+16 -16
View File
@@ -1,5 +1,5 @@
/**
* Cyrene DevTools - 主入口
* Cyrene ethend - 主入口
*
* 提供:
* - REST API: 服务管理状态查询性能分析健康检查代理
@@ -17,7 +17,7 @@ import { execSync, spawn } from 'child_process';
import { processManager } from './process-manager.js';
import { performanceMonitor } from './performance.js';
import { SERVICES, DEVTOOLS_PORT, LOGS_DIR, logFile, GATEWAY_URL, PLUGIN_MANAGER_URL, ADMIN_USERNAME, ADMIN_PASSWORD } from './config.js';
import { SERVICES, ETHEND_PORT, LOGS_DIR, logFile, GATEWAY_URL, PLUGIN_MANAGER_URL, ADMIN_USERNAME, ADMIN_PASSWORD } from './config.js';
const AI_CORE_URL = process.env.AI_CORE_URL || 'http://localhost:8081';
const MEMORY_SERVICE_URL = process.env.MEMORY_SERVICE_URL || 'http://localhost:8091';
@@ -209,15 +209,15 @@ async function proxyToAICore(path, opts = {}) {
app.get('/api/health', (_req, res) => {
res.json({
status: 'ok',
service: 'cyrene-devtools',
service: 'cyrene-ethend',
uptime: process.uptime(),
wsClients: wsClients.size,
});
});
// ---- DevTools 自重启 ----
app.post('/api/devtools/restart', (_req, res) => {
res.json({ success: true, message: 'DevTools 正在重启...' });
// ---- ethend 自重启 ----
app.post('/api/ethend/restart', (_req, res) => {
res.json({ success: true, message: 'ethend 正在重启...' });
// 延迟 500ms 确保响应已发送,然后 spawn 新进程并退出
setTimeout(() => {
@@ -918,7 +918,7 @@ const sttLogEntries = [];
const MAX_STT_LOGS = 200;
/**
* 记录 STT 请求日志devtools 自身维护因为 voice-service 无持久化日志
* 记录 STT 请求日志ethend 自身维护因为 voice-service 无持久化日志
*/
function recordSTTLog(entry) {
sttLogEntries.unshift(entry);
@@ -969,7 +969,7 @@ app.post('/api/voice/transcribe', async (req, res) => {
try {
// 构建 multipart/form-data 请求转发到 Voice-Service
const boundary = '----DevToolsFormBoundary' + Math.random().toString(36).slice(2);
const boundary = '----ethendFormBoundary' + Math.random().toString(36).slice(2);
const crlf = '\r\n';
const headerParts = [
'--' + boundary + crlf,
@@ -1026,7 +1026,7 @@ app.post('/api/voice/transcribe', async (req, res) => {
recordSTTLog(logEntry);
return res.status(voiceResp.status).json({
...voiceBody,
devtools_log_id: logEntry.id,
ethend_log_id: logEntry.id,
});
}
@@ -1045,7 +1045,7 @@ app.post('/api/voice/transcribe', async (req, res) => {
recordSTTLog(logEntry);
return res.json({
...voiceBody,
devtools_log_id: logEntry.id,
ethend_log_id: logEntry.id,
});
} catch (err) {
const elapsedMs = Date.now() - startTime;
@@ -1070,7 +1070,7 @@ app.post('/api/voice/transcribe', async (req, res) => {
hint: isConnRefused
? 'Voice-Service 服务未启动,请先在「服务管理」面板中启动 Voice-Service'
: 'Voice-Service 服务无响应,请检查网络连接和服务状态',
devtools_log_id: logEntry.id,
ethend_log_id: logEntry.id,
});
}
});
@@ -1725,11 +1725,11 @@ performanceMonitor.start();
// 确保日志目录存在
fs.mkdirSync(LOGS_DIR, { recursive: true });
server.listen(DEVTOOLS_PORT, () => {
console.log(`🛠️ Cyrene DevTools 已启动: http://localhost:${DEVTOOLS_PORT}`);
console.log(` API: http://localhost:${DEVTOOLS_PORT}/api/health`);
console.log(` WebSocket: ws://localhost:${DEVTOOLS_PORT}/ws`);
console.log(` Web控制台: http://localhost:${DEVTOOLS_PORT}`);
server.listen(ETHEND_PORT, () => {
console.log(`🛠️ Cyrene ethend 已启动: http://localhost:${ETHEND_PORT}`);
console.log(` API: http://localhost:${ETHEND_PORT}/api/health`);
console.log(` WebSocket: ws://localhost:${ETHEND_PORT}/ws`);
console.log(` Web控制台: http://localhost:${ETHEND_PORT}`);
console.log('');
console.log(' 可用服务:');
for (const [id, svc] of Object.entries(SERVICES)) {
+17
View File
@@ -0,0 +1,17 @@
// 页面路由 Store — 管理当前显示的页面
import { create } from 'zustand';
export type PageId = 'chat' | 'admin-models' | 'admin-dashboard' | 'profile';
interface PageState {
currentPage: PageId;
setPage: (page: PageId) => void;
goToChat: () => void;
}
export const usePageStore = create<PageState>((set) => ({
currentPage: 'chat',
setPage: (page) => set({ currentPage: page }),
goToChat: () => set({ currentPage: 'chat' }),
}));
+1 -1
View File
@@ -55,7 +55,7 @@ tar -czf "${OUTPUT_DIR}/${ARCHIVE_NAME}" \
--exclude='frontend/web/node_modules' \
--exclude='frontend/web/dist' \
--exclude='frontend/node_modules' \
--exclude='devtools/node_modules' \
--exclude='ethend/node_modules' \
--exclude='frontend/packages/*/node_modules' \
--exclude='.env' \
--exclude='backend/.env' \