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:
@@ -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
@@ -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/
|
||||
|
||||
@@ -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
@@ -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 会自动先编译再启动。
|
||||
|
||||
如需手动逐个启动:
|
||||
|
||||
|
||||
@@ -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.
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
Vendored
+1
-1
@@ -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",
|
||||
|
||||
Vendored
+1
-1
@@ -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 {}})
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/*` |
|
||||
@@ -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 admin,DevTools 代理提供 UI):
|
||||
**配置管理 API**(Gateway admin,ethend 代理提供 UI):
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
|
||||
@@ -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 (内部) | 向量数据库 |
|
||||
|
||||
@@ -85,7 +85,7 @@ HOST_EXEC_MAX_TIMEOUT=300
|
||||
|
||||
## 验证
|
||||
|
||||
用 DevTools 或 API 测试:
|
||||
用 ethend 或 API 测试:
|
||||
|
||||
```bash
|
||||
# 通过 API 发送消息,让昔涟使用 os_exec
|
||||
|
||||
+28
-28
@@ -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
@@ -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
|
||||
@@ -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';
|
||||
@@ -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)) {
|
||||
@@ -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
@@ -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' \
|
||||
|
||||
Reference in New Issue
Block a user