Skip to content

Commit 972b9dc

Browse files
authored
Merge pull request Stack-Cairn#234 from yyg-max/feat/multi-agent
feat: support multiple agents with isolated v2 sessions and WebUI management
2 parents 58f53d4 + 1347b4d commit 972b9dc

175 files changed

Lines changed: 12698 additions & 4529 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 1 addition & 57 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Dockerfile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ COPY --from=gateway-builder /out/liveagent-gateway /usr/local/bin/liveagent-gate
4040

4141
USER liveagent
4242

43-
ENV PORT=8080
43+
ENV PORT=8080 \
44+
LIVEAGENT_GATEWAY_DATA_DIR=/var/lib/liveagent
45+
46+
VOLUME ["/var/lib/liveagent"]
4447

4548
EXPOSE 8080
4649

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,12 @@ proto:
133133
cd $(AGENT_GATEWAY_DIR) && buf generate
134134

135135
# buf breaking 的对比基线(本地默认与当前 HEAD 对比;CI 覆写为 origin/main)。
136-
BUF_BREAKING_AGAINST ?= ../../.git#subdir=$(AGENT_GATEWAY_DIR)
136+
BUF_BREAKING_AGAINST ?= ../../.git\#subdir=$(AGENT_GATEWAY_DIR)
137137

138138
proto-check:
139139
@command -v buf >/dev/null || (echo "buf is required. Run: mise install" && exit 1)
140140
cd $(AGENT_GATEWAY_DIR) && buf lint
141-
cd $(AGENT_GATEWAY_DIR) && buf breaking --against '$(BUF_BREAKING_AGAINST)'
141+
node scripts/check-buf-breaking.mjs "$(AGENT_GATEWAY_DIR)" "$(BUF_BREAKING_AGAINST)"
142142

143143
webui:
144144
pnpm --dir $(AGENT_GATEWAY_WEB_DIR) install --offline

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,13 @@ docker run -d \
177177
--name liveagent-gateway \
178178
--restart unless-stopped \
179179
-p 3000:8080 \
180+
-v liveagent-gateway-data:/var/lib/liveagent \
180181
-e LIVEAGENT_GATEWAY_TOKEN=your-token \
181182
ghcr.io/stack-cairn/liveagent-gateway:latest
182183
```
183184

185+
The named volume persists the Gateway database and independently issued Agent tokens across container upgrades.
186+
184187
**One-command upgrade to the latest version** — pull the new image → remove the old container → recreate it with the same arguments (if you changed the port mappings or token, adjust the arguments below accordingly):
185188

186189
```bash
@@ -190,6 +193,7 @@ docker pull ghcr.io/stack-cairn/liveagent-gateway:latest \
190193
--name liveagent-gateway \
191194
--restart unless-stopped \
192195
-p 3000:8080 \
196+
-v liveagent-gateway-data:/var/lib/liveagent \
193197
-e LIVEAGENT_GATEWAY_TOKEN=your-token \
194198
ghcr.io/stack-cairn/liveagent-gateway:latest \
195199
&& docker image prune -f
@@ -321,7 +325,7 @@ LiveAgent/
321325
│ └── agent-gateway/ # Go gateway service
322326
│ ├── cmd/gateway/ # Entry point
323327
│ ├── internal/ # Core implementation
324-
│ ├── proto/v1/ # Protobuf definitions
328+
│ ├── proto/v2/ # Protobuf definitions
325329
│ └── web/ # Embedded WebUI
326330
327331
├── docs/ # Project docs

README.zh-CN.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,10 +178,13 @@ docker run -d \
178178
--name liveagent-gateway \
179179
--restart unless-stopped \
180180
-p 3000:8080 \
181+
-v liveagent-gateway-data:/var/lib/liveagent \
181182
-e LIVEAGENT_GATEWAY_TOKEN=your-token \
182183
ghcr.io/stack-cairn/liveagent-gateway:latest
183184
```
184185

186+
命名卷用于持久化 Gateway 数据库和独立签发的 Agent Token,重建容器时不会丢失。
187+
185188
**一键升级到最新版** — 拉取新镜像 → 删除旧容器 → 以相同参数重建(若你修改过端口映射或 token,请同步替换下方参数):
186189

187190
```bash
@@ -191,6 +194,7 @@ docker pull ghcr.io/stack-cairn/liveagent-gateway:latest \
191194
--name liveagent-gateway \
192195
--restart unless-stopped \
193196
-p 3000:8080 \
197+
-v liveagent-gateway-data:/var/lib/liveagent \
194198
-e LIVEAGENT_GATEWAY_TOKEN=your-token \
195199
ghcr.io/stack-cairn/liveagent-gateway:latest \
196200
&& docker image prune -f
@@ -321,7 +325,7 @@ LiveAgent/
321325
│ └── agent-gateway/ # Go 网关服务
322326
│ ├── cmd/gateway/ # 入口
323327
│ ├── internal/ # 核心实现
324-
│ ├── proto/v1/ # Protobuf 定义
328+
│ ├── proto/v2/ # Protobuf 定义
325329
│ └── web/ # 嵌入式 WebUI
326330
327331
├── docs/ # 项目文档

crates/agent-gateway/buf.yaml

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
# buf 模块配置(v2 schema)。模块根设在 crates/agent-gateway/ 而非 proto/,保持 import 路径
2-
# "proto/v1/gateway.proto" 与历史 protoc 及 Rust build.rs 一致,Go 生成物与既有提交字节级一致。
1+
# buf 模块配置(v2 schema)。模块根设在 crates/agent-gateway/,供 v2 帧壳导入共享业务消息。
32
version: v2
43
modules:
54
- path: .
@@ -12,13 +11,12 @@ lint:
1211
except:
1312
# 历史布局 proto/<版本>/ 早于 buf;迁目录会改 import 路径、破坏 Rust build.rs 与生成物,不翻新。
1413
- PACKAGE_DIRECTORY_MATCH
15-
# v1 契约早于 buf lint,历史命名不翻新(wire 兼容优先);豁免为全量 lint 确认后的最小集,
16-
# 仅限 v1 文件——v2 及新增 proto 必须完整满足 STANDARD。
14+
# 共享业务消息沿用已发布的枚举命名;只对该迁移文件保留最小豁免。
1715
ignore_only:
1816
ENUM_VALUE_PREFIX:
19-
- proto/v1/gateway.proto
17+
- proto/v2/gateway.proto
2018
ENUM_ZERO_VALUE_SUFFIX:
21-
- proto/v1/gateway.proto
19+
- proto/v2/gateway.proto
2220
breaking:
2321
use:
2422
# WIRE_JSON:兼守二进制与 JSON 线格式兼容(chat 事件与公开分享页仍走 JSON 载荷)。

crates/agent-gateway/cmd/gateway/main.go

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import (
1010
"syscall"
1111
"time"
1212

13+
"github.com/liveagent/agent-gateway/internal/auth/agenttoken"
1314
"github.com/liveagent/agent-gateway/internal/config"
15+
"github.com/liveagent/agent-gateway/internal/db"
1416
"github.com/liveagent/agent-gateway/internal/observability"
1517
"github.com/liveagent/agent-gateway/internal/server"
1618
"github.com/liveagent/agent-gateway/internal/session"
@@ -25,16 +27,31 @@ func fatal(msg string, args ...any) {
2527
func main() {
2628
observability.SetupLogging()
2729
cfg := config.Load()
28-
//nolint:staticcheck // 读取弃用字段正是为了对旧启动脚本发出弃用警告。
29-
if cfg.GRPCAddr != "" {
30-
slog.Warn("-grpc-addr is deprecated and ignored: the v1 gRPC listener was removed; desktop clients connect via /ws/v2/agent on the HTTP port")
31-
}
3230
sm := session.NewManager()
3331

32+
// 统一连接池:库由 internal/db 打开并集中管理,各持久化子系统在共享池上
33+
// 初始化自己的表;main 持有生命周期(退出时统一关闭)。
34+
database, err := db.Open(cfg.AgentDB)
35+
if err != nil {
36+
fatal("open gateway db failed", "path", cfg.AgentDB, "err", err)
37+
}
38+
defer func() { _ = database.Close() }()
39+
40+
tokens, err := agenttoken.NewStore(database)
41+
if err != nil {
42+
fatal("init agent token store failed", "err", err)
43+
}
44+
slog.Info("agent registry db ready", "path", cfg.AgentDB)
45+
slog.Info("agent authentication accepts gateway token or per-agent token")
46+
3447
httpServer := &http.Server{
3548
Addr: cfg.HTTPAddr,
36-
Handler: server.NewHTTPServer(cfg, sm),
49+
Handler: server.NewHTTPServer(cfg, sm, tokens),
3750
ReadHeaderTimeout: 10 * time.Second,
51+
// 空闲 keep-alive 连接必须回收,否则 REST/静态资源访问方挂住连接会把 fd
52+
// 慢性耗尽到 ulimit。刻意不设全局 Read/WriteTimeout:流式上传与隧道长响应
53+
// 需要;WS 连接已被 hijack、自管理超时,不受影响。
54+
IdleTimeout: 120 * time.Second,
3855
}
3956

4057
errCh := make(chan error, 1)

crates/agent-gateway/go.mod

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,17 @@ require (
1010
github.com/tdewolff/parse/v2 v2.8.13
1111
golang.org/x/net v0.57.0
1212
google.golang.org/protobuf v1.36.11
13+
modernc.org/sqlite v1.54.0
14+
)
15+
16+
require (
17+
github.com/dustin/go-humanize v1.0.1 // indirect
18+
github.com/mattn/go-isatty v0.0.20 // indirect
19+
github.com/ncruces/go-strftime v1.0.0 // indirect
20+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
21+
golang.org/x/sync v0.22.0 // indirect
22+
golang.org/x/sys v0.47.0 // indirect
23+
modernc.org/libc v1.74.1 // indirect
24+
modernc.org/mathutil v1.7.1 // indirect
25+
modernc.org/memory v1.11.0 // indirect
1326
)

crates/agent-gateway/go.sum

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,67 @@
11
github.com/doyensec/safeurl v0.2.5 h1:kKu0JNQy0tJ8jkDyB5h6Aml9vWWniq+mpoa12EGLcOQ=
22
github.com/doyensec/safeurl v0.2.5/go.mod h1:3H0cgRpPYPSpgxRRn5yGD35Ns/LgGX/BVWSBbzUqXtY=
3+
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
4+
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
35
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
46
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
57
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
68
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
9+
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
10+
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
711
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
812
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
913
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
1014
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
15+
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
16+
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
17+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
18+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
19+
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
20+
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
21+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
22+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
1123
github.com/tdewolff/parse/v2 v2.8.13 h1:si/8rLw5BZZTWCCiMm9A3f6x+RmqYfrkEeXCgpX5ick=
1224
github.com/tdewolff/parse/v2 v2.8.13/go.mod h1:XdsoSFThlVIRIajAuqz1evNY7bagZS8LBOPA3aVopwQ=
1325
github.com/tdewolff/test v1.0.12 h1:7F21DqIajswxuche0geHdrUZRCWE4oko4b7bcmkkrxk=
1426
github.com/tdewolff/test v1.0.12/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
27+
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
28+
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
1529
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
1630
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
31+
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
32+
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
33+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
34+
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
35+
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
36+
golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q=
37+
golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA=
1738
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
1839
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
40+
modernc.org/cc/v4 v4.29.0 h1:CXgwL8cvxmyzBQZzbSl/6xFtMCryb6u8IOqDci39cgc=
41+
modernc.org/cc/v4 v4.29.0/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
42+
modernc.org/ccgo/v4 v4.34.6 h1:sBgfIwyN0TQ9C5hwIeuqyeAKyMWnbvj2fvpF4L11uzU=
43+
modernc.org/ccgo/v4 v4.34.6/go.mod h1:SZ8YcN9NG7XVsQYdm6jYBvi8PQP1qi+kqB6OhjqI3Fk=
44+
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
45+
modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
46+
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
47+
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
48+
modernc.org/gc/v3 v3.1.4 h1:2g65LGVSmFQrXeITAw97x7hCRvZFcyE1uDP+7Vng7JI=
49+
modernc.org/gc/v3 v3.1.4/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
50+
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
51+
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
52+
modernc.org/libc v1.74.1 h1:bdR4VTKFMC4966QSNZ05XLGI/VwzVa2kTUX51Dm0riQ=
53+
modernc.org/libc v1.74.1/go.mod h1:uH4t5bOx3G3g9Xcmj10YKlTcVISlRDwv8VoQJG9n8Os=
54+
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
55+
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
56+
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
57+
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
58+
modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg=
59+
modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
60+
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
61+
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
62+
modernc.org/sqlite v1.54.0 h1:JCxR4qwkJvOaqAoYcgDoO25Nc+ROg6EJ2LfBVzdrgog=
63+
modernc.org/sqlite v1.54.0/go.mod h1:4ntCLuNmnH8+GNqjka1wNg7KJd5/Hi5FYp8K+XQ7GZw=
64+
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
65+
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
66+
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
67+
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=

0 commit comments

Comments
 (0)