Ver código fonte

前端页面修改

moki 12 horas atrás
pai
commit
e4a024d89d

+ 19 - 14
cmd/monitor/main.go

@@ -306,7 +306,8 @@ func printConfigUsage() {
 	fmt.Println("BLE 选项:")
 	fmt.Println("BLE 选项:")
 	fmt.Println("  --device          蓝牙设备名称 (必填)")
 	fmt.Println("  --device          蓝牙设备名称 (必填)")
 	fmt.Println("  --service-uuid    BLE 服务 UUID (必填)")
 	fmt.Println("  --service-uuid    BLE 服务 UUID (必填)")
-	fmt.Println("  --char-uuid       BLE 特征 UUID (必填)")
+	fmt.Println("  --mode-char-uuid  BLE 模式特征 UUID (必填)")
+	fmt.Println("  --config-char-uuid BLE 配置特征 UUID (必填)")
 	fmt.Println("  --enabled         是否启用 (true/false)")
 	fmt.Println("  --enabled         是否启用 (true/false)")
 	fmt.Println("")
 	fmt.Println("")
 	fmt.Println("全局选项:")
 	fmt.Println("全局选项:")
@@ -345,7 +346,8 @@ func startBLERelay(bleCfg *database.BLEConfig, ctx context.Context) io.WriteClos
 	args := []string{
 	args := []string{
 		"--device", bleCfg.DeviceName,
 		"--device", bleCfg.DeviceName,
 		"--service-uuid", bleCfg.ServiceUUID,
 		"--service-uuid", bleCfg.ServiceUUID,
-		"--char-uuid", bleCfg.CharUUID,
+		"--mode-char-uuid", bleCfg.ModeCharUUID,
+		"--config-char-uuid", bleCfg.ConfigCharUUID,
 	}
 	}
 
 
 	cmd := exec.CommandContext(ctx, tmpExe, args...)
 	cmd := exec.CommandContext(ctx, tmpExe, args...)
@@ -399,28 +401,30 @@ func runBleConfig(db *database.DB, args []string) {
 			if cfg.Enabled {
 			if cfg.Enabled {
 				status = "启用"
 				status = "启用"
 			}
 			}
-			fmt.Printf("[%d] %s | %s | %s | %s\n", cfg.ID, cfg.DeviceName, cfg.ServiceUUID, cfg.CharUUID, status)
+			fmt.Printf("[%d] %s | %s | %s | %s | %s\n", cfg.ID, cfg.DeviceName, cfg.ServiceUUID, cfg.ModeCharUUID, cfg.ConfigCharUUID, status)
 		}
 		}
 
 
 	case "set":
 	case "set":
 		fs := flag.NewFlagSet("config ble set", flag.ExitOnError)
 		fs := flag.NewFlagSet("config ble set", flag.ExitOnError)
 		deviceName := fs.String("device", "", "蓝牙设备名称 (必填)")
 		deviceName := fs.String("device", "", "蓝牙设备名称 (必填)")
 		serviceUUID := fs.String("service-uuid", "", "BLE 服务 UUID (必填)")
 		serviceUUID := fs.String("service-uuid", "", "BLE 服务 UUID (必填)")
-		charUUID := fs.String("char-uuid", "", "BLE 特征 UUID (必填)")
+		modeCharUUID := fs.String("mode-char-uuid", "", "BLE 模式特征 UUID (必填)")
+		configCharUUID := fs.String("config-char-uuid", "", "BLE 配置特征 UUID (必填)")
 		enabled := fs.Bool("enabled", true, "是否启用")
 		enabled := fs.Bool("enabled", true, "是否启用")
 		fs.Parse(args[1:])
 		fs.Parse(args[1:])
 
 
-		if *deviceName == "" || *serviceUUID == "" || *charUUID == "" {
-			fmt.Println("错误: --device, --service-uuid, --char-uuid 为必填参数")
+		if *deviceName == "" || *serviceUUID == "" || *modeCharUUID == "" || *configCharUUID == "" {
+			fmt.Println("错误: --device, --service-uuid, --mode-char-uuid, --config-char-uuid 为必填参数")
 			fs.Usage()
 			fs.Usage()
 			return
 			return
 		}
 		}
 
 
 		cfg := &database.BLEConfig{
 		cfg := &database.BLEConfig{
-			DeviceName:  *deviceName,
-			ServiceUUID: *serviceUUID,
-			CharUUID:    *charUUID,
-			Enabled:     *enabled,
+			DeviceName:     *deviceName,
+			ServiceUUID:    *serviceUUID,
+			ModeCharUUID:   *modeCharUUID,
+			ConfigCharUUID: *configCharUUID,
+			Enabled:        *enabled,
 		}
 		}
 
 
 		if err := db.SaveBLEConfig(cfg); err != nil {
 		if err := db.SaveBLEConfig(cfg); err != nil {
@@ -464,8 +468,9 @@ func printBleConfigUsage() {
 	fmt.Println("  delete <id>       删除 BLE 配置")
 	fmt.Println("  delete <id>       删除 BLE 配置")
 	fmt.Println("")
 	fmt.Println("")
 	fmt.Println("选项:")
 	fmt.Println("选项:")
-	fmt.Println("  --device          蓝牙设备名称 (必填)")
-	fmt.Println("  --service-uuid    BLE 服务 UUID (必填)")
-	fmt.Println("  --char-uuid       BLE 特征 UUID (必填)")
-	fmt.Println("  --enabled         是否启用 (true/false)")
+	fmt.Println("  --device            蓝牙设备名称 (必填)")
+	fmt.Println("  --service-uuid      BLE 服务 UUID (必填)")
+	fmt.Println("  --mode-char-uuid    BLE 模式特征 UUID (必填)")
+	fmt.Println("  --config-char-uuid  BLE 配置特征 UUID (必填)")
+	fmt.Println("  --enabled           是否启用 (true/false)")
 }
 }

+ 54 - 0
hooks/claude-code/settings.json

@@ -0,0 +1,54 @@
+{
+  "hooks": {
+    "SessionStart": [
+      {
+        "hooks": [
+          {
+            "type": "http",
+            "url": "http://localhost:8045/api/event",
+            "timeout": 5,
+            "async": true
+          }
+        ]
+      }
+    ],
+    "PreToolUse": [
+      {
+        "matcher": "Bash|Edit|Write|NotebookEdit",
+        "hooks": [
+          {
+            "type": "http",
+            "url": "http://localhost:8045/api/event",
+            "timeout": 5,
+            "async": true
+          }
+        ]
+      }
+    ],
+    "PostToolUse": [
+      {
+        "matcher": "Bash|Edit|Write|NotebookEdit",
+        "hooks": [
+          {
+            "type": "http",
+            "url": "http://localhost:8045/api/event",
+            "timeout": 5,
+            "async": true
+          }
+        ]
+      }
+    ],
+    "Stop": [
+      {
+        "hooks": [
+          {
+            "type": "http",
+            "url": "http://localhost:8045/api/event",
+            "timeout": 5,
+            "async": true
+          }
+        ]
+      }
+    ]
+  }
+}

+ 50 - 0
hooks/codex/hooks.json

@@ -0,0 +1,50 @@
+{
+  "hooks": {
+    "SessionStart": [
+      {
+        "hooks": [
+          {
+            "type": "command",
+            "command": "curl -s -X POST http://localhost:8045/api/event -H 'Content-Type: application/json' -d '{\"code\":\"init\",\"source\":\"codex\"}' &",
+            "timeout": 5
+          }
+        ]
+      }
+    ],
+    "PreToolUse": [
+      {
+        "matcher": "Bash|apply_patch",
+        "hooks": [
+          {
+            "type": "command",
+            "command": "curl -s -X POST http://localhost:8045/api/event -H 'Content-Type: application/json' -d '{\"code\":\"using_tool\",\"source\":\"codex\"}' &",
+            "timeout": 5
+          }
+        ]
+      }
+    ],
+    "PostToolUse": [
+      {
+        "matcher": "Bash|apply_patch",
+        "hooks": [
+          {
+            "type": "command",
+            "command": "curl -s -X POST http://localhost:8045/api/event -H 'Content-Type: application/json' -d '{\"code\":\"tool_done\",\"source\":\"codex\"}' &",
+            "timeout": 5
+          }
+        ]
+      }
+    ],
+    "Stop": [
+      {
+        "hooks": [
+          {
+            "type": "command",
+            "command": "curl -s -X POST http://localhost:8045/api/event -H 'Content-Type: application/json' -d '{\"code\":\"idle\",\"source\":\"codex\"}' &",
+            "timeout": 5
+          }
+        ]
+      }
+    ]
+  }
+}

+ 116 - 0
hooks/install.sh

@@ -0,0 +1,116 @@
+#!/bin/bash
+
+# AI Status Light - Hooks 安装脚本
+# 支持 OpenCode、Claude Code、Codex
+
+set -e
+
+SERVICE_URL="${STATUS_LIGHT_URL:-http://localhost:8045}"
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+
+echo "=== AI Status Light Hooks 安装 ==="
+echo "服务地址: $SERVICE_URL"
+echo ""
+
+# OpenCode 插件
+install_opencode() {
+    echo "安装 OpenCode 插件..."
+    mkdir -p ~/.config/opencode/plugins
+    cp "$SCRIPT_DIR/opencode-plugin/status-light.ts" ~/.config/opencode/plugins/
+    
+    # 更新配置
+    mkdir -p ~/.config/opencode
+    cat > ~/.config/opencode/status-light.json << EOF
+{
+  "serviceUrl": "$SERVICE_URL"
+}
+EOF
+    echo "✅ OpenCode 插件已安装"
+}
+
+# Claude Code hooks
+install_claude_code() {
+    echo "安装 Claude Code hooks..."
+    mkdir -p ~/.claude
+    
+    # 读取模板并替换 URL
+    if [ -f ~/.claude/settings.json ]; then
+        echo "⚠️  ~/.claude/settings.json 已存在,请手动合并 hooks 配置"
+        echo "配置文件: $SCRIPT_DIR/hooks/claude-code/settings.json"
+    else
+        sed "s|http://localhost:8045|$SERVICE_URL|g" \
+            "$SCRIPT_DIR/hooks/claude-code/settings.json" > ~/.claude/settings.json
+        echo "✅ Claude Code hooks 已安装"
+    fi
+}
+
+# Codex hooks
+install_codex() {
+    echo "安装 Codex hooks..."
+    mkdir -p ~/.codex
+    
+    # 读取模板并替换 URL
+    if [ -f ~/.codex/hooks.json ]; then
+        echo "⚠️  ~/.codex/hooks.json 已存在,请手动合并 hooks 配置"
+        echo "配置文件: $SCRIPT_DIR/hooks/codex/hooks.json"
+    else
+        sed "s|http://localhost:8045|$SERVICE_URL|g" \
+            "$SCRIPT_DIR/hooks/codex/hooks.json" > ~/.codex/hooks.json
+        echo "✅ Codex hooks 已安装"
+    fi
+}
+
+# 解析参数
+INSTALL_ALL=false
+INSTALL_OPENCODE=false
+INSTALL_CLAUDE=false
+INSTALL_CODEX=false
+
+if [ $# -eq 0 ]; then
+    INSTALL_ALL=true
+fi
+
+for arg in "$@"; do
+    case $arg in
+        --all)
+            INSTALL_ALL=true
+            ;;
+        --opencode)
+            INSTALL_OPENCODE=true
+            ;;
+        --claude)
+            INSTALL_CLAUDE=true
+            ;;
+        --codex)
+            INSTALL_CODEX=true
+            ;;
+        --help|-h)
+            echo "用法: $s0 [选项]"
+            echo ""
+            echo "选项:"
+            echo "  --all       安装所有 hooks (默认)"
+            echo "  --opencode  仅安装 OpenCode 插件"
+            echo "  --claude    仅安装 Claude Code hooks"
+            echo "  --codex     仅安装 Codex hooks"
+            echo "  --help      显示帮助"
+            exit 0
+            ;;
+        *)
+            echo "未知选项: $arg"
+            exit 1
+            ;;
+    esac
+done
+
+if [ "$INSTALL_ALL" = true ]; then
+    install_opencode
+    install_claude_code
+    install_codex
+else
+    [ "$INSTALL_OPENCODE" = true ] && install_opencode
+    [ "$INSTALL_CLAUDE" = true ] && install_claude_code
+    [ "$INSTALL_CODEX" = true ] && install_codex
+fi
+
+echo ""
+echo "安装完成!请重启对应的 AI 工具以使 hooks 生效。"

+ 4 - 4
internal/api/api.go

@@ -390,8 +390,8 @@ func (s *Server) createBLEConfig(w http.ResponseWriter, r *http.Request) {
 		return
 		return
 	}
 	}
 
 
-	if cfg.DeviceName == "" || cfg.ServiceUUID == "" || cfg.CharUUID == "" {
-		writeJSON(w, http.StatusBadRequest, Response{Code: -1, Message: "device_name, service_uuid, char_uuid 不能为空"})
+	if cfg.DeviceName == "" || cfg.ServiceUUID == "" || cfg.ModeCharUUID == "" || cfg.ConfigCharUUID == "" {
+		writeJSON(w, http.StatusBadRequest, Response{Code: -1, Message: "device_name, service_uuid, mode_char_uuid, config_char_uuid 不能为空"})
 		return
 		return
 	}
 	}
 
 
@@ -435,8 +435,8 @@ func (s *Server) updateBLEConfig(w http.ResponseWriter, r *http.Request, id int)
 
 
 	cfg.ID = id
 	cfg.ID = id
 
 
-	if cfg.DeviceName == "" || cfg.ServiceUUID == "" || cfg.CharUUID == "" {
-		writeJSON(w, http.StatusBadRequest, Response{Code: -1, Message: "device_name, service_uuid, char_uuid 不能为空"})
+	if cfg.DeviceName == "" || cfg.ServiceUUID == "" || cfg.ModeCharUUID == "" || cfg.ConfigCharUUID == "" {
+		writeJSON(w, http.StatusBadRequest, Response{Code: -1, Message: "device_name, service_uuid, mode_char_uuid, config_char_uuid 不能为空"})
 		return
 		return
 	}
 	}
 
 

+ 17 - 15
internal/database/database.go

@@ -28,11 +28,12 @@ type MQTTConfig struct {
 }
 }
 
 
 type BLEConfig struct {
 type BLEConfig struct {
-	ID          int    `json:"id"`
-	DeviceName  string `json:"device_name"`
-	ServiceUUID string `json:"service_uuid"`
-	CharUUID    string `json:"char_uuid"`
-	Enabled     bool   `json:"enabled"`
+	ID             int    `json:"id"`
+	DeviceName     string `json:"device_name"`
+	ServiceUUID    string `json:"service_uuid"`
+	ModeCharUUID   string `json:"mode_char_uuid"`
+	ConfigCharUUID string `json:"config_char_uuid"`
+	Enabled        bool   `json:"enabled"`
 }
 }
 
 
 func New(dbPath string) (*DB, error) {
 func New(dbPath string) (*DB, error) {
@@ -90,7 +91,8 @@ func (d *DB) init() error {
 		id INTEGER PRIMARY KEY AUTOINCREMENT,
 		id INTEGER PRIMARY KEY AUTOINCREMENT,
 		device_name TEXT NOT NULL,
 		device_name TEXT NOT NULL,
 		service_uuid TEXT NOT NULL,
 		service_uuid TEXT NOT NULL,
-		char_uuid TEXT NOT NULL,
+		mode_char_uuid TEXT NOT NULL DEFAULT '',
+		config_char_uuid TEXT NOT NULL DEFAULT '',
 		enabled BOOLEAN DEFAULT 1
 		enabled BOOLEAN DEFAULT 1
 	);
 	);
 	`
 	`
@@ -99,7 +101,7 @@ func (d *DB) init() error {
 		return err
 		return err
 	}
 	}
 
 
-	for _, col := range []string{"service_uuid", "char_uuid"} {
+	for _, col := range []string{"service_uuid", "mode_char_uuid", "config_char_uuid"} {
 		alterQuery := fmt.Sprintf("ALTER TABLE ble_config ADD COLUMN %s TEXT NOT NULL DEFAULT ''", col)
 		alterQuery := fmt.Sprintf("ALTER TABLE ble_config ADD COLUMN %s TEXT NOT NULL DEFAULT ''", col)
 		d.conn.ExecContext(context.Background(), alterQuery)
 		d.conn.ExecContext(context.Background(), alterQuery)
 	}
 	}
@@ -184,11 +186,11 @@ func (d *DB) Close() error {
 }
 }
 
 
 func (d *DB) GetBLEConfig() (*BLEConfig, error) {
 func (d *DB) GetBLEConfig() (*BLEConfig, error) {
-	query := "SELECT id, device_name, service_uuid, char_uuid, enabled FROM ble_config WHERE enabled = 1 LIMIT 1"
+	query := "SELECT id, device_name, service_uuid, mode_char_uuid, config_char_uuid, enabled FROM ble_config WHERE enabled = 1 LIMIT 1"
 	row := d.conn.QueryRowContext(context.Background(), query)
 	row := d.conn.QueryRowContext(context.Background(), query)
 
 
 	var cfg BLEConfig
 	var cfg BLEConfig
-	err := row.Scan(&cfg.ID, &cfg.DeviceName, &cfg.ServiceUUID, &cfg.CharUUID, &cfg.Enabled)
+	err := row.Scan(&cfg.ID, &cfg.DeviceName, &cfg.ServiceUUID, &cfg.ModeCharUUID, &cfg.ConfigCharUUID, &cfg.Enabled)
 	if err == sql.ErrNoRows {
 	if err == sql.ErrNoRows {
 		logger.Debug("未找到启用的 BLE 配置")
 		logger.Debug("未找到启用的 BLE 配置")
 		return nil, nil
 		return nil, nil
@@ -203,15 +205,15 @@ func (d *DB) GetBLEConfig() (*BLEConfig, error) {
 
 
 func (d *DB) SaveBLEConfig(cfg *BLEConfig) error {
 func (d *DB) SaveBLEConfig(cfg *BLEConfig) error {
 	if cfg.ID == 0 {
 	if cfg.ID == 0 {
-		query := "INSERT INTO ble_config (device_name, service_uuid, char_uuid, enabled) VALUES (?, ?, ?, ?)"
-		_, err := d.conn.ExecContext(context.Background(), query, cfg.DeviceName, cfg.ServiceUUID, cfg.CharUUID, cfg.Enabled)
+		query := "INSERT INTO ble_config (device_name, service_uuid, mode_char_uuid, config_char_uuid, enabled) VALUES (?, ?, ?, ?, ?)"
+		_, err := d.conn.ExecContext(context.Background(), query, cfg.DeviceName, cfg.ServiceUUID, cfg.ModeCharUUID, cfg.ConfigCharUUID, cfg.Enabled)
 		if err != nil {
 		if err != nil {
 			logger.Error("插入 BLE 配置失败: %v", err)
 			logger.Error("插入 BLE 配置失败: %v", err)
 		}
 		}
 		return err
 		return err
 	}
 	}
-	query := "UPDATE ble_config SET device_name = ?, service_uuid = ?, char_uuid = ?, enabled = ? WHERE id = ?"
-	_, err := d.conn.ExecContext(context.Background(), query, cfg.DeviceName, cfg.ServiceUUID, cfg.CharUUID, cfg.Enabled, cfg.ID)
+	query := "UPDATE ble_config SET device_name = ?, service_uuid = ?, mode_char_uuid = ?, config_char_uuid = ?, enabled = ? WHERE id = ?"
+	_, err := d.conn.ExecContext(context.Background(), query, cfg.DeviceName, cfg.ServiceUUID, cfg.ModeCharUUID, cfg.ConfigCharUUID, cfg.Enabled, cfg.ID)
 	if err != nil {
 	if err != nil {
 		logger.Error("更新 BLE 配置失败: id=%d, %v", cfg.ID, err)
 		logger.Error("更新 BLE 配置失败: id=%d, %v", cfg.ID, err)
 	}
 	}
@@ -230,7 +232,7 @@ func (d *DB) DeleteBLEConfig(id int) error {
 }
 }
 
 
 func (d *DB) ListBLEConfigs() ([]BLEConfig, error) {
 func (d *DB) ListBLEConfigs() ([]BLEConfig, error) {
-	query := "SELECT id, device_name, service_uuid, char_uuid, enabled FROM ble_config ORDER BY id"
+	query := "SELECT id, device_name, service_uuid, mode_char_uuid, config_char_uuid, enabled FROM ble_config ORDER BY id"
 	rows, err := d.conn.QueryContext(context.Background(), query)
 	rows, err := d.conn.QueryContext(context.Background(), query)
 	if err != nil {
 	if err != nil {
 		logger.Error("查询 BLE 配置列表失败: %v", err)
 		logger.Error("查询 BLE 配置列表失败: %v", err)
@@ -241,7 +243,7 @@ func (d *DB) ListBLEConfigs() ([]BLEConfig, error) {
 	var configs []BLEConfig
 	var configs []BLEConfig
 	for rows.Next() {
 	for rows.Next() {
 		var cfg BLEConfig
 		var cfg BLEConfig
-		if err := rows.Scan(&cfg.ID, &cfg.DeviceName, &cfg.ServiceUUID, &cfg.CharUUID, &cfg.Enabled); err != nil {
+		if err := rows.Scan(&cfg.ID, &cfg.DeviceName, &cfg.ServiceUUID, &cfg.ModeCharUUID, &cfg.ConfigCharUUID, &cfg.Enabled); err != nil {
 			logger.Warn("扫描 BLE 配置行失败: %v", err)
 			logger.Warn("扫描 BLE 配置行失败: %v", err)
 			continue
 			continue
 		}
 		}