File size: 3,204 Bytes
6a7089a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package main

import (
	"fmt"
	"os"

	"github.com/pinchtab/pinchtab/internal/cli"
	"github.com/pinchtab/pinchtab/internal/config"
	"github.com/pinchtab/pinchtab/internal/server"
	"github.com/spf13/cobra"
)

var version = "dev"

var rootCmd = &cobra.Command{
	Use:   "pinchtab",
	Short: "PinchTab - Browser control for AI agents",
	Long: `PinchTab provides a lightweight, API-driven way for AI agents to control
browsers, manage tabs, and perform interactive tasks.`,
	Example: `  pinchtab server
  pinchtab nav https://pinchtab.com`,
	Run: func(cmd *cobra.Command, args []string) {
		cfg := config.Load()

		// Check if security wizard needs to run
		maybeRunWizard()

		if isInteractiveTerminal() {
			cli.PrintStartupBanner(cfg, cli.StartupBannerOptions{
				Mode:         "menu",
				ListenStatus: menuListenStatus(cfg),
			})

			picked, err := promptSelect("Main Menu", []menuOption{
				{label: "Start server", value: "server"},
				{label: "Daemon", value: "daemon"},
				{label: "Start bridge", value: "bridge"},
				{label: "Start MCP server", value: "mcp"},
				{label: "Config", value: "config"},
				{label: "Security", value: "security"},
				{label: "Help", value: "help"},
				{label: "Exit", value: "exit"},
			})

			if err != nil || picked == "exit" || picked == "" {
				return
			}

			switch picked {
			case "server":
				server.RunDashboard(cfg, version)
			case "daemon":
				handleDaemonCommand(cfg, "")
			case "bridge":
				server.RunBridgeServer(cfg)
			case "mcp":
				runMCP(cfg)
			case "config":
				handleConfigOverview(cfg)
			case "security":
				handleSecurityCommand(cfg)
			case "help":
				_ = cmd.Help()
			}
			return
		}

		// Fallback for non-interactive: start the server
		server.RunDashboard(cfg, version)
	},
}

// maybeRunWizard checks if the security wizard should run and triggers it.
func maybeRunWizard() {
	fileCfg, configPath, err := config.LoadFileConfig()
	if err != nil || configPath == "" {
		return // No config file context — skip wizard
	}

	if !config.NeedsWizard(fileCfg) {
		return
	}

	isNew := config.IsFirstRun(fileCfg)
	runSecurityWizard(fileCfg, configPath, isNew)
}

func menuListenStatus(cfg *config.RuntimeConfig) string {
	dashPort := cfg.Port
	if dashPort == "" {
		dashPort = "9870"
	}
	if server.CheckPinchTabRunning(dashPort, cfg.Token) {
		return "running"
	}
	return "stopped"
}

func Execute() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

// serverURL is the global --server flag for CLI commands
var serverURL string

func init() {
	rootCmd.Version = version
	rootCmd.SetVersionTemplate("pinchtab {{.Version}}\n")

	// Global flags
	rootCmd.PersistentFlags().StringVar(&serverURL, "server", "", "PinchTab server URL (default: http://127.0.0.1:{port})")

	// Grouping commands
	primaryGroup := &cobra.Group{ID: "primary", Title: "Primary Commands"}
	browserGroup := &cobra.Group{ID: "browser", Title: "Browser Control"}
	mgmtGroup := &cobra.Group{ID: "management", Title: "Profiles and Instances"}
	configGroup := &cobra.Group{ID: "config", Title: "Configuration & Setup"}

	rootCmd.AddGroup(primaryGroup, browserGroup, mgmtGroup, configGroup)

	cli.SetupUsage(rootCmd)
}