mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
5e028a847c
A standalone web-based tool for managing picoclaw configuration, OAuth authentication providers, and gateway process lifecycle. Features include a sidebar layout with i18n (en/zh) and theme support, real-time gateway log streaming, startup prerequisites checks, and Windows icon embedding. Co-authored-by: wj-xiao <meetwenjie@gmail.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
128 lines
3.6 KiB
Go
128 lines
3.6 KiB
Go
// PicoClaw Launcher - Standalone HTTP service
|
|
//
|
|
// Provides a web-based JSON editor for picoclaw config files,
|
|
// with OAuth provider authentication support.
|
|
//
|
|
// Usage:
|
|
//
|
|
// go build -o picoclaw-launcher ./cmd/picoclaw-launcher/
|
|
// ./picoclaw-launcher [config.json]
|
|
// ./picoclaw-launcher -public config.json
|
|
|
|
package main
|
|
|
|
import (
|
|
"embed"
|
|
"flag"
|
|
"fmt"
|
|
"io/fs"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
"time"
|
|
|
|
"github.com/sipeed/picoclaw/cmd/picoclaw-launcher/internal/server"
|
|
)
|
|
|
|
//go:embed internal/ui/index.html
|
|
var staticFiles embed.FS
|
|
|
|
func main() {
|
|
public := flag.Bool("public", false, "Listen on all interfaces (0.0.0.0) instead of localhost only")
|
|
flag.Usage = func() {
|
|
fmt.Fprintf(os.Stderr, "PicoClaw Launcher - A web-based configuration editor\n\n")
|
|
fmt.Fprintf(os.Stderr, "Usage: %s [options] [config.json]\n\n", os.Args[0])
|
|
fmt.Fprintf(os.Stderr, "Arguments:\n")
|
|
fmt.Fprintf(os.Stderr, " config.json Path to the configuration file (default: ~/.picoclaw/config.json)\n\n")
|
|
fmt.Fprintf(os.Stderr, "Options:\n")
|
|
flag.PrintDefaults()
|
|
fmt.Fprintf(os.Stderr, "\nExamples:\n")
|
|
fmt.Fprintf(os.Stderr, " %s Use default config path\n", os.Args[0])
|
|
fmt.Fprintf(os.Stderr, " %s ./config.json Specify a config file\n", os.Args[0])
|
|
fmt.Fprintf(
|
|
os.Stderr,
|
|
" %s -public ./config.json Allow access from other devices on the network\n",
|
|
os.Args[0],
|
|
)
|
|
}
|
|
flag.Parse()
|
|
|
|
configPath := server.DefaultConfigPath()
|
|
if flag.NArg() > 0 {
|
|
configPath = flag.Arg(0)
|
|
}
|
|
|
|
absPath, err := filepath.Abs(configPath)
|
|
if err != nil {
|
|
log.Fatalf("Failed to resolve config path: %v", err)
|
|
}
|
|
|
|
var addr string
|
|
if *public {
|
|
addr = "0.0.0.0:" + server.DefaultPort
|
|
} else {
|
|
addr = "127.0.0.1:" + server.DefaultPort
|
|
}
|
|
|
|
mux := http.NewServeMux()
|
|
server.RegisterConfigAPI(mux, absPath)
|
|
server.RegisterAuthAPI(mux, absPath)
|
|
server.RegisterProcessAPI(mux, absPath)
|
|
|
|
staticFS, err := fs.Sub(staticFiles, "internal/ui")
|
|
if err != nil {
|
|
log.Fatalf("Failed to create sub filesystem: %v", err)
|
|
}
|
|
mux.Handle("/", http.FileServer(http.FS(staticFS)))
|
|
|
|
// Print startup banner
|
|
fmt.Println("=============================================")
|
|
fmt.Println(" PicoClaw Launcher")
|
|
fmt.Println("=============================================")
|
|
fmt.Printf(" Config file : %s\n", absPath)
|
|
fmt.Printf(" Listen addr : %s\n\n", addr)
|
|
fmt.Println(" Open the following URL in your browser")
|
|
fmt.Println(" to view and edit the configuration:")
|
|
fmt.Println()
|
|
fmt.Printf(" >> http://localhost:%s <<\n", server.DefaultPort)
|
|
if *public {
|
|
if ip := server.GetLocalIP(); ip != "" {
|
|
fmt.Printf(" >> http://%s:%s <<\n", ip, server.DefaultPort)
|
|
}
|
|
}
|
|
fmt.Println()
|
|
// fmt.Println("=============================================")
|
|
|
|
go func() {
|
|
// Wait briefly to ensure the server is ready before opening the browser
|
|
time.Sleep(500 * time.Millisecond)
|
|
url := "http://localhost:" + server.DefaultPort
|
|
if err := openBrowser(url); err != nil {
|
|
log.Printf("Warning: Failed to auto-open browser: %v\n", err)
|
|
}
|
|
}()
|
|
|
|
if err := http.ListenAndServe(addr, mux); err != nil {
|
|
log.Fatalf("Server failed: %v", err)
|
|
}
|
|
}
|
|
|
|
// openBrowser automatically opens the given URL in the default browser.
|
|
func openBrowser(url string) error {
|
|
var err error
|
|
switch runtime.GOOS {
|
|
case "linux":
|
|
err = exec.Command("xdg-open", url).Start()
|
|
case "windows":
|
|
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
|
|
case "darwin":
|
|
err = exec.Command("open", url).Start()
|
|
default:
|
|
err = fmt.Errorf("unsupported platform")
|
|
}
|
|
return err
|
|
}
|