diff --git a/cmd/gpu-turnstile/main.go b/cmd/gpu-turnstile/main.go index 2c67d71..f6bf311 100644 --- a/cmd/gpu-turnstile/main.go +++ b/cmd/gpu-turnstile/main.go @@ -44,9 +44,9 @@ func stdoutIsTerminal() bool { } // parseFlags extracts -config (or -config=), the -// --install-service / --remove-service switches, --no-copy, -h/--help and -// the hidden --elevated-child marker from args. -func parseFlags(args []string) (configPath string, install, remove, noCopy, help, elevatedChild bool, rest []string) { +// --install-service / --remove-service switches, --no-copy, -h/--help, +// -v/--version and the hidden --elevated-child marker from args. +func parseFlags(args []string) (configPath string, install, remove, noCopy, help, showVersion, elevatedChild bool, rest []string) { rest = args[:0] for i := 0; i < len(args); i++ { switch { @@ -63,21 +63,27 @@ func parseFlags(args []string) (configPath string, install, remove, noCopy, help noCopy = true case args[i] == "-h" || args[i] == "--help" || args[i] == "-help": help = true + case args[i] == "-v" || args[i] == "--version" || args[i] == "-version": + showVersion = true case args[i] == "--elevated-child": elevatedChild = true default: rest = append(rest, args[i]) } } - return configPath, install, remove, noCopy, help, elevatedChild, rest + return configPath, install, remove, noCopy, help, showVersion, elevatedChild, rest } -const usageText = `gpu-turnstile — GPU arbitration proxy for Ollama + ComfyUI +// versionLine is printed at the top of every help and error screen. +func versionLine() string { return "gpu-turnstile " + version } + +const usageText = `GPU arbitration proxy for Ollama + ComfyUI Usage: gpu-turnstile -config run the proxy gpu-turnstile --install-service [--no-copy] [-config path] install + start as a service gpu-turnstile --remove-service stop + uninstall the service + gpu-turnstile -v | --version print just the version gpu-turnstile -h | --help this help Options: @@ -92,22 +98,40 @@ All runtime settings are environment variables or KEY=VALUE lines in the config file (OLLAMA_URL, COMFY_URL, LOGLEVEL, ...); see README.md. ` +// printHelp prints the version header plus the full help text. +func printHelp() { + fmt.Printf("%s — %s", versionLine(), usageText) +} + +// fatalUsage prints the version header, an error message and the one-line +// usage summary, then exits with code 2. +func fatalUsage(format string, args ...any) { + fmt.Fprintf(os.Stderr, "%s\n\n", versionLine()) + fmt.Fprintf(os.Stderr, format+"\n\n", args...) + fmt.Fprintln(os.Stderr, "usage: gpu-turnstile [-config path] [--install-service [--no-copy] | --remove-service]") + fmt.Fprintln(os.Stderr, " gpu-turnstile service install|remove [-config path]") + os.Exit(2) +} + func main() { - configPath, install, remove, noCopy, help, elevatedChild, args := parseFlags(os.Args[1:]) + configPath, install, remove, noCopy, help, showVersion, elevatedChild, args := parseFlags(os.Args[1:]) + if showVersion { + fmt.Println(version) + return + } bare := configPath == "" && !install && !remove && !elevatedChild && len(args) == 0 if help || (bare && stdoutIsTerminal()) { // Bare invocation in a terminal (e.g. double-clicked on Windows) // shows the help instead of starting a proxy window with no visible // explanation. Without a terminal — Docker containers, services, // pipes — a bare invocation starts the proxy as before. - fmt.Print(usageText) + printHelp() return } if len(args) > 0 && args[0] == "service" { // Legacy subcommand form: gpu-turnstile service install|remove. if len(args) != 2 || (args[1] != "install" && args[1] != "remove") { - fmt.Fprintf(os.Stderr, "usage: gpu-turnstile service install|remove [-config path]\n") - os.Exit(2) + fatalUsage("error: expected 'service install' or 'service remove'") } install = args[1] == "install" remove = !install @@ -115,17 +139,14 @@ func main() { } switch { case install && remove: - fmt.Fprintf(os.Stderr, "gpu-turnstile: --install-service and --remove-service are mutually exclusive\n") - os.Exit(2) + fatalUsage("error: --install-service and --remove-service are mutually exclusive") case install: os.Exit(serviceCommand(configPath, true, noCopy, elevatedChild)) case remove: os.Exit(serviceCommand(configPath, false, noCopy, elevatedChild)) } if len(args) > 0 { - fmt.Fprintf(os.Stderr, "usage: gpu-turnstile [-config path] [--install-service [--no-copy] | --remove-service]\n") - fmt.Fprintf(os.Stderr, " gpu-turnstile service install|remove [-config path]\n") - os.Exit(2) + fatalUsage("error: unknown arguments: %s", strings.Join(args, " ")) } if exePath, err := os.Executable(); err == nil { @@ -134,7 +155,7 @@ func main() { cfg, err := loadMergedConfig(configPath) if err != nil { - fmt.Fprintf(os.Stderr, "gpu-turnstile: %v\n", err) + fmt.Fprintf(os.Stderr, "%s\n\ngpu-turnstile: %v\n", versionLine(), err) os.Exit(1) } log, logOut, logCloser := newLogger(cfg)