moki 3 недель назад
Родитель
Сommit
72ca47f995
5 измененных файлов с 76 добавлено и 38 удалено
  1. 15 10
      README.md
  2. 39 13
      cmd/monitor/main.go
  3. 16 12
      docs/api.md
  4. 2 1
      internal/api/api.go
  5. 4 2
      internal/event/event.go

+ 15 - 10
README.md

@@ -117,22 +117,27 @@ curl http://localhost:8080/api/mqtt
 {
   "port": 4096,
   "status": "忙碌",
+  "code": "busy",
   "timestamp": "2026-06-03T14:30:00Z"
 }
 ```
 
 ### 推送的状态类型
 
-| 状态 | 说明 |
-|------|------|
-| 空闲 | 会话空闲 |
-| 忙碌 | 会话忙碌 |
-| 重试中 | 会话重试中 |
-| 修改中 | 会话修改中 |
-| 思考中 | 模型推理中 |
-| 运行中 | 工具执行中 |
-| 完成 | 工具执行完成 |
-| 错误 | 会话错误 |
+| status | code | 说明 |
+|--------|------|------|
+| 空闲 | idle | 会话空闲 |
+| 忙碌 | busy | 会话忙碌 |
+| 重试中 | retry | 会话重试中 |
+| 修改中 | pending | 会话修改中 |
+| 思考中 | reasoning | 模型推理中 |
+| 使用工具中 | using_tool | AI 正在使用工具 |
+| 等待中 | pending | 工具等待执行 |
+| 运行中 | running | 工具执行中 |
+| 工具执行完成 | completed | 工具执行完成 |
+| 会话完成 | session_completed | 会话执行完成 |
+| 等待权限 | permission | AI 向用户申请权限 |
+| 错误 | error | 会话错误 |
 
 ## 命令行参数
 

+ 39 - 13
cmd/monitor/main.go

@@ -282,6 +282,26 @@ func printConfigUsage() {
 }
 
 func createCallback(mqttClient *mqttcli.Client, apiServer *api.Server) monitor.EventCallback {
+	lastStatus := make(map[int]string)
+	var mu sync.Mutex
+
+	publish := func(port int, status string, code string) {
+		if mqttClient != nil {
+			payload := map[string]interface{}{
+				"port":      port,
+				"status":    status,
+				"code":      code,
+				"timestamp": time.Now().Format(time.RFC3339),
+			}
+			if err := mqttClient.PublishRaw(mqttClient.GetTopic(), payload); err != nil {
+				fmt.Printf("MQTT 发送失败: %v\n", err)
+			}
+		}
+		if apiServer != nil {
+			apiServer.BroadcastStatus(port, status, code)
+		}
+	}
+
 	return func(port int, evt *event.SSEEvent) {
 		msg := event.FormatEvent(port, evt)
 		if msg != "" {
@@ -292,49 +312,55 @@ func createCallback(mqttClient *mqttcli.Client, apiServer *api.Server) monitor.E
 			return
 		}
 
-		var status string
+		var status, code string
 
 		switch evt.Type {
 		case "session.status":
 			if s, ok := evt.Properties["status"].(map[string]interface{}); ok {
+				if t, ok := s["type"].(string); ok {
+					code = t
+				}
 				status = event.ParseStatus(s)
 			}
 		case "session.idle":
 			status = "空闲"
+			code = "idle"
 		case "message.part.updated":
 			if part, ok := evt.Properties["part"].(map[string]interface{}); ok {
 				switch part["type"].(string) {
 				case "tool":
 					if st, ok := part["state"].(map[string]interface{}); ok {
+						if s, ok := st["status"].(string); ok {
+							code = s
+						}
 						status = event.ParseToolState(st)
 					}
 				case "reasoning":
 					status = "思考中"
+					code = "reasoning"
 				default:
 					status = "使用工具中"
+					code = "using_tool"
 				}
 			}
 		case "permission.updated":
 			status = "等待权限"
+			code = "permission"
 		case "session.error":
 			status = "错误"
+			code = "error"
 		}
 
 		if status != "" {
-			if mqttClient != nil {
-				payload := map[string]interface{}{
-					"port":      port,
-					"status":    status,
-					"timestamp": time.Now().Format(time.RFC3339),
-				}
-				if err := mqttClient.PublishRaw(mqttClient.GetTopic(), payload); err != nil {
-					fmt.Printf("MQTT 发送失败: %v\n", err)
-				}
+			mu.Lock()
+			prev := lastStatus[port]
+			if status == "空闲" && prev != "" && prev != "空闲" {
+				publish(port, "会话完成", "session_completed")
 			}
+			lastStatus[port] = status
+			mu.Unlock()
 
-			if apiServer != nil {
-				apiServer.BroadcastStatus(port, status)
-			}
+			publish(port, status, code)
 		}
 	}
 }

+ 16 - 12
docs/api.md

@@ -210,24 +210,27 @@ API 已启用 CORS,支持跨域请求。
 {
   "port": 4096,
   "status": "忙碌",
+  "code": "busy",
   "timestamp": "2026-06-03T14:30:00Z"
 }
 ```
 
 ### 推送的状态类型
 
-| 状态 | 说明 |
-|------|------|
-| 空闲 | 会话空闲 |
-| 忙碌 | 会话忙碌 |
-| 重试中 | 会话重试中 |
-| 修改中 | 会话修改中 |
-| 思考中 | 模型推理中 |
-| 使用工具中 | AI 正在使用工具 |
-| 运行中 | 工具执行中 |
-| 完成 | 工具执行完成 |
-| 等待权限 | AI 向用户申请权限 |
-| 错误 | 会话错误 |
+| status | code | 说明 |
+|--------|------|------|
+| 空闲 | idle | 会话空闲 |
+| 忙碌 | busy | 会话忙碌 |
+| 重试中 | retry | 会话重试中 |
+| 修改中 | pending | 会话修改中 |
+| 思考中 | reasoning | 模型推理中 |
+| 使用工具中 | using_tool | AI 正在使用工具 |
+| 等待中 | pending | 工具等待执行 |
+| 运行中 | running | 工具执行中 |
+| 工具执行完成 | completed | 工具执行完成 |
+| 会话完成 | session_completed | 会话执行完成 |
+| 等待权限 | permission | AI 向用户申请权限 |
+| 错误 | error | 会话错误 |
 
 ## WebSocket 实时状态
 
@@ -247,6 +250,7 @@ WebSocket 推送的消息格式为 JSON:
 {
   "port": 4096,
   "status": "忙碌",
+  "code": "busy",
   "timestamp": "2026-06-03T14:30:00Z"
 }
 ```

+ 2 - 1
internal/api/api.go

@@ -237,7 +237,7 @@ func (s *Server) handleWebSocket(w http.ResponseWriter, r *http.Request) {
 	}()
 }
 
-func (s *Server) BroadcastStatus(port int, status string) {
+func (s *Server) BroadcastStatus(port int, status string, code string) {
 	s.clientsMu.Lock()
 	defer s.clientsMu.Unlock()
 
@@ -248,6 +248,7 @@ func (s *Server) BroadcastStatus(port int, status string) {
 	payload := map[string]interface{}{
 		"port":      port,
 		"status":    status,
+		"code":      code,
 		"timestamp": time.Now().Format(time.RFC3339),
 	}
 

+ 4 - 2
internal/event/event.go

@@ -42,6 +42,8 @@ func ParseToolState(state map[string]interface{}) string {
 	s, _ := state["status"].(string)
 	title, _ := state["title"].(string)
 	switch s {
+	case "pending":
+		return "等待中"
 	case "running":
 		if title != "" {
 			return "运行中: " + title
@@ -49,9 +51,9 @@ func ParseToolState(state map[string]interface{}) string {
 		return "运行中"
 	case "completed":
 		if title != "" {
-			return "完成: " + title
+			return "工具执行完成: " + title
 		}
-		return "完成"
+		return "工具执行完成"
 	case "error":
 		return "错误"
 	default: