package main import ( "fmt" "os" "strings" ) const appVersion = "2.1.0" // 会话级命名互斥体(Local 前缀无需特权;Global 前缀普通用户常创建失败,会导致单实例失效) const mutexName = "Local\\DeepSeekTraySingleton" func main() { args := os.Args[1:] cfg := loadConfig() log := NewLogger() defer log.Close() log.Printf("=== deepseek-tray %s args=%v ===", appVersion, args) if len(args) > 0 { switch strings.ToLower(strings.TrimLeft(args[0], "-/")) { case "version": attachConsole() fmt.Println(appVersion) return case "status": attachConsole() printStatus(cfg, log) return case "autostart": attachConsole() runAutoStartCommand(args, log) return case "daemon": // 无 UI 守护模式(可注册为 Windows 服务):不弹终端、不打开浏览器 runDaemon(cfg, log) return case "silent": // 静默启动(开机自启使用):等同托盘模式,全程无窗口、不打开浏览器 } } inst, ok := acquireSingleInstance(mutexName) if !ok { attachConsole() fmt.Println("deepseek-tray 已在运行(托盘或守护进程)") return } defer inst.release() runTray(cfg, log) } // runDaemon 无 UI 守护模式(适合 NSSM 注册为 Windows 服务)。 func runDaemon(cfg *Config, log *Logger) { log.Printf("以守护模式启动(无托盘)") inst, ok := acquireSingleInstance(mutexName) if !ok { log.Printf("已有实例在运行,退出") return } defer inst.release() sup := NewSupervisor(cfg, log) sup.Start() if cfg.AutoUpdate { sup.startAutoUpdate() } select {} } // printStatus 命令行状态查询(同时写日志,便于无控制台场景回溯)。 func printStatus(cfg *Config, log *Logger) { sup := NewSupervisor(cfg, log) version := sup.installedVersion() if version == "" { version = "未知(未找到 dsh)" } lines := []string{ fmt.Sprintf("deepseek-tray %s", appVersion), fmt.Sprintf("DSH 版本 : %s", version), fmt.Sprintf("Web 地址 : http://%s:%d", cfg.WebHost, cfg.WebPort), fmt.Sprintf("端口监听 : %v", isPortOpen(cfg.WebHost, cfg.WebPort)), fmt.Sprintf("开机自启 : %v", isAutoStartEnabled()), fmt.Sprintf("定期自动更新: %v(间隔 %d 小时)", cfg.AutoUpdate, cfg.UpdateIntervalH), fmt.Sprintf("配置文件 : %s", configPath()), fmt.Sprintf("日志目录 : %s", logDir()), } for _, line := range lines { fmt.Println(line) log.Printf("status | %s", line) } } // runAutoStartCommand 处理 autostart [on|off] 子命令(无 UI 场景配置自启)。 func runAutoStartCommand(args []string, log *Logger) { sub := "" if len(args) > 1 { sub = strings.ToLower(args[1]) } switch sub { case "on": if err := setAutoStart(true); err != nil { fmt.Println("设置开机自启失败:", err) log.Printf("autostart on 失败: %v", err) return } fmt.Println("开机自启:已开启(静默启动,无窗口、不打开浏览器)") log.Printf("autostart on") case "off": if err := setAutoStart(false); err != nil { fmt.Println("取消开机自启失败:", err) log.Printf("autostart off 失败: %v", err) return } fmt.Println("开机自启:已关闭") log.Printf("autostart off") default: fmt.Printf("开机自启:%v\n", isAutoStartEnabled()) fmt.Println("用法: deepseek-tray.exe autostart [on|off]") log.Printf("autostart status: %v", isAutoStartEnabled()) } }