From d961ba1ec7bc4e784eefa878e2a6e1e023873a74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=AC=E6=98=8E?= <83812544+Ed1s0nZ@users.noreply.github.com> Date: Fri, 15 May 2026 11:43:33 +0800 Subject: [PATCH] Add files via upload --- README.md | 12 ++++++++---- README_CN.md | 12 ++++++++---- cmd/server/main.go | 43 ++++++++++++++++++++++++++++++++++++++++--- web/static/js/chat.js | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 736fbd3b..14a8f4ce 100644 --- a/README.md +++ b/README.md @@ -174,9 +174,11 @@ The `run.sh` script will automatically: - ✅ Build the project - ✅ Start the server +**Networking defaults:** `run.sh` starts the server with **`--https`** and the repo **`config.yaml`** (local self-signed TLS; better for many concurrent streams). Use **`./run.sh --http`** for plain HTTP. In production, set **`server.tls_cert_path`** / **`server.tls_key_path`** in **`config.yaml`** (see comments there). For manual runs, add **`--https`** or **`CYBERSTRIKE_HTTPS=1`**; if **`-config`** is wrong, the binary prints a short usage hint on stderr. + **First-Time Configuration:** 1. **Configure OpenAI-compatible API** (required before first use) - - Open http://localhost:8080 after launch + - After launch, open **`https://127.0.0.1:8080/`** (or **`https://localhost:8080/`**; replace **8080** with `server.port` in `config.yaml`) and accept the self-signed certificate warning once. If you used `./run.sh --http`, use **`http://`** instead. - Go to `Settings` → Fill in your API credentials: ```yaml openai: @@ -197,14 +199,16 @@ The `run.sh` script will automatically: **Alternative Launch Methods:** ```bash -# Direct Go run (requires manual setup) -go run cmd/server/main.go +# Direct Go run (set up env yourself); add --https to match run.sh defaults +go run cmd/server/main.go --https # Manual build go build -o cyberstrike-ai cmd/server/main.go -./cyberstrike-ai +./cyberstrike-ai --https ``` +If server logs show `client sent an HTTP request to an HTTPS server`, a client is still using **`http://`** on a TLS-only port—switch the URL to **`https://`**. + **Note:** The Python virtual environment (`venv/`) is automatically created and managed by `run.sh`. Tools that require Python (like `api-fuzzer`, `http-framework-test`, etc.) will automatically use this environment. ### Version Update (No Breaking Changes) diff --git a/README_CN.md b/README_CN.md index 2e7bb04f..db2d00c6 100644 --- a/README_CN.md +++ b/README_CN.md @@ -173,9 +173,11 @@ chmod +x run.sh && ./run.sh - ✅ 编译构建项目 - ✅ 启动服务器 +**网络默认:** `run.sh` 会以 **`--https`** 并传入项目根 **`config.yaml`** 启动(本机自签证书,多路流式场景更稳)。只要明文 HTTP 用 **`./run.sh --http`**。生产环境在 **`config.yaml`** 的 **`server.tls_cert_path` / `server.tls_key_path`** 配正式证书(见文件内注释)。手动启动可加 **`--https`** 或环境变量 **`CYBERSTRIKE_HTTPS=1`**;`-config` 写错时程序会在终端提示正确写法。 + **首次配置:** 1. **配置 AI 模型 API**(首次使用前必填) - - 启动后访问 http://localhost:8080 + - 启动后在浏览器打开 **`https://127.0.0.1:8080/`**(或 **`https://localhost:8080/`**;端口以 `config.yaml` 中 **`server.port`** 为准,默认 8080),并按提示信任自签证书。若使用 **`./run.sh --http`**,则改用 **`http://`** 访问。 - 进入 `设置` → 填写 API 配置信息: ```yaml openai: @@ -196,14 +198,16 @@ chmod +x run.sh && ./run.sh **其他启动方式:** ```bash -# 直接运行(需手动配置环境) -go run cmd/server/main.go +# 直接运行(需自行配环境);与 run.sh 默认一致可加 --https +go run cmd/server/main.go --https # 手动编译 go build -o cyberstrike-ai cmd/server/main.go -./cyberstrike-ai +./cyberstrike-ai --https ``` +若日志出现 `client sent an HTTP request to an HTTPS server`,说明仍有客户端用 **`http://`** 访问只提供 HTTPS 的端口,请改为 **`https://`**。 + **说明:** Python 虚拟环境(`venv/`)由 `run.sh` 自动创建和管理。需要 Python 的工具(如 `api-fuzzer`、`http-framework-test` 等)会自动使用该环境。 ### CyberStrikeAI 版本更新(无兼容性问题) diff --git a/cmd/server/main.go b/cmd/server/main.go index ce21de8c..dd24460d 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -9,22 +9,59 @@ import ( "fmt" "os" "os/signal" + "strings" "syscall" ) func main() { var configPath = flag.String("config", "config.yaml", "配置文件路径") + var httpsBootstrap = flag.Bool("https", false, "启用主站 HTTPS:未配置 tls_cert_path/tls_key_path 时使用内存自签证书(本地测试);与 run.sh 默认行为一致") flag.Parse() + // 环境变量兼容(便于 systemd/docker 等不传参场景) + if !*httpsBootstrap { + v := strings.TrimSpace(os.Getenv("CYBERSTRIKE_HTTPS")) + if v == "1" || strings.EqualFold(v, "true") || strings.EqualFold(v, "yes") { + *httpsBootstrap = true + } + } + // 加载配置 - cfg, err := config.Load(*configPath) + cp := strings.TrimSpace(*configPath) + if cp == "" { + cp = "config.yaml" + } + if strings.HasPrefix(cp, "-") { + fmt.Fprintf(os.Stderr, "无效的 -config 路径 %q。\n若同时需要 HTTPS,请写成: ./cyberstrike-ai --https -config config.yaml(-config 后必须是 yaml 文件路径)。\n", cp) + os.Exit(2) + } + cfg, err := config.Load(cp) if err != nil { fmt.Printf("加载配置失败: %v\n", err) return } + if *httpsBootstrap { + config.ApplyDevHTTPSBootstrap(cfg) + } + + port := cfg.Server.Port + if port <= 0 { + port = 8080 + } + scheme := "http" + if config.MainWebUIUsesHTTPS(&cfg.Server) { + scheme = "https" + } + fmt.Println() + fmt.Printf("→ Web 界面: %s://127.0.0.1:%d/\n", scheme, port) + if scheme == "https" && cfg.Server.TLSAutoSelfSign { + fmt.Println(" (内存自签证书:浏览器首次需确认「继续访问」)") + } + fmt.Println() + // MCP 启用且 auth_header_value 为空时,自动生成随机密钥并写回配置 - if err := config.EnsureMCPAuth(*configPath, cfg); err != nil { + if err := config.EnsureMCPAuth(cp, cfg); err != nil { fmt.Printf("MCP 鉴权配置失败: %v\n", err) return } @@ -44,7 +81,7 @@ func main() { signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) // 创建应用 - application, err := app.New(cfg, log) + application, err := app.New(cfg, log, cp) if err != nil { log.Fatal("应用初始化失败", "error", err) } diff --git a/web/static/js/chat.js b/web/static/js/chat.js index ec2cbec2..d551be19 100644 --- a/web/static/js/chat.js +++ b/web/static/js/chat.js @@ -2224,6 +2224,39 @@ function showCopySuccess(button) { } } +/** 相邻且类型/正文/data 完全一致的过程详情只保留一条(与后端去重一致,避免时间线叠多条相同块) */ +function dedupeConsecutiveProcessDetailRows(details) { + if (!Array.isArray(details) || details.length < 2) { + return details; + } + const out = [details[0]]; + for (let i = 1; i < details.length; i++) { + const cur = details[i]; + if (processDetailRowFingerprint(out[out.length - 1]) === processDetailRowFingerprint(cur)) { + continue; + } + out.push(cur); + } + return out; +} + +function processDetailRowFingerprint(d) { + if (!d || typeof d !== 'object') { + return ''; + } + const et = String(d.eventType || ''); + const msg = String(d.message != null ? d.message : '').trim(); + let dataKey = ''; + try { + if (d.data != null) { + dataKey = JSON.stringify(d.data); + } + } catch (e) { + dataKey = String(d.data); + } + return et + '\0' + msg + '\0' + dataKey; +} + // 渲染过程详情 function renderProcessDetails(messageId, processDetails) { const messageElement = document.getElementById(messageId); @@ -2323,6 +2356,7 @@ function renderProcessDetails(messageId, processDetails) { } detailsContainer.dataset.lazyNotLoaded = '0'; detailsContainer.dataset.loaded = '1'; + processDetails = dedupeConsecutiveProcessDetailRows(processDetails); // 如果没有processDetails或为空,显示空状态 if (!processDetails || processDetails.length === 0) { // 显示空状态提示