Skip to content

Commit b303f1c

Browse files
committed
refactor: simplify config with flags/env and server-managed worknode info
- Remove config package in favor of CLI flags with environment variable fallback - Worknode name and labels are now managed via Web UI and sent in welcome message - Authentication uses token query parameter instead of header - Add structured logging for MCP client operations for debugging - Remove update command (not implemented) Breaking changes: - Config file no longer supported, use --token flag or FLASHDUTY_RUNNER_TOKEN env - Worknode name/labels configured on server side, not client config
1 parent 4479254 commit b303f1c

10 files changed

Lines changed: 263 additions & 475 deletions

File tree

cmd/main.go

Lines changed: 100 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ import (
77
"log/slog"
88
"os"
99
"os/signal"
10+
"path/filepath"
1011
"syscall"
1112
"time"
1213

1314
"github.com/spf13/cobra"
1415

15-
"github.com/flashcatcloud/flashduty-runner/config"
1616
"github.com/flashcatcloud/flashduty-runner/permission"
1717
"github.com/flashcatcloud/flashduty-runner/workspace"
1818
"github.com/flashcatcloud/flashduty-runner/ws"
@@ -25,7 +25,19 @@ var (
2525
GitCommit = "unknown"
2626
)
2727

28-
var configPath string
28+
// Command line flags
29+
var (
30+
flagToken string
31+
flagURL string
32+
flagWorkspace string
33+
flagLogLevel string
34+
)
35+
36+
// Default values
37+
const (
38+
defaultURL = "wss://api.flashcat.cloud/safari/worknode/ws"
39+
defaultLogLevel = "info"
40+
)
2941

3042
func main() {
3143
rootCmd := &cobra.Command{
@@ -38,28 +50,47 @@ It connects to Flashduty platform via WebSocket and executes workspace operation
3850
(bash, read, write, list, glob, grep, webfetch) and MCP tool calls.`,
3951
}
4052

41-
// Global flags
42-
rootCmd.PersistentFlags().StringVarP(&configPath, "config", "c", "", "config file path (default: ~/.flashduty-runner/config.yaml)")
43-
4453
// Add subcommands
4554
rootCmd.AddCommand(runCmd())
4655
rootCmd.AddCommand(versionCmd())
47-
rootCmd.AddCommand(updateCmd())
4856

4957
if err := rootCmd.Execute(); err != nil {
5058
os.Exit(1)
5159
}
5260
}
5361

5462
func runCmd() *cobra.Command {
55-
return &cobra.Command{
63+
cmd := &cobra.Command{
5664
Use: "run",
5765
Short: "Start the runner and connect to Flashduty",
58-
Long: `Start the runner, connect to Flashduty cloud via WebSocket, and begin processing tasks.`,
66+
Long: `Start the runner, connect to Flashduty cloud via WebSocket, and begin processing tasks.
67+
68+
Examples:
69+
# Basic usage (token required)
70+
flashduty-runner run --token wnt_xxx
71+
72+
# Specify workspace directory
73+
flashduty-runner run --token wnt_xxx --workspace ~/projects
74+
75+
# Specify custom API URL
76+
flashduty-runner run --token wnt_xxx --url wss://custom.example.com/safari/worknode/ws
77+
78+
Environment variables:
79+
FLASHDUTY_RUNNER_TOKEN - Authentication token (required if --token not provided)
80+
FLASHDUTY_RUNNER_URL - WebSocket endpoint URL
81+
FLASHDUTY_RUNNER_WORKSPACE - Workspace root directory`,
5982
RunE: func(cmd *cobra.Command, args []string) error {
6083
return runRunner()
6184
},
6285
}
86+
87+
// Flags with environment variable fallback
88+
cmd.Flags().StringVar(&flagToken, "token", "", "Authentication token (required, env: FLASHDUTY_RUNNER_TOKEN)")
89+
cmd.Flags().StringVar(&flagURL, "url", "", "WebSocket endpoint URL (env: FLASHDUTY_RUNNER_URL)")
90+
cmd.Flags().StringVar(&flagWorkspace, "workspace", "", "Workspace root directory (env: FLASHDUTY_RUNNER_WORKSPACE)")
91+
cmd.Flags().StringVar(&flagLogLevel, "log-level", "", "Log level: debug, info, warn, error (env: FLASHDUTY_RUNNER_LOG_LEVEL)")
92+
93+
return cmd
6394
}
6495

6596
func versionCmd() *cobra.Command {
@@ -74,35 +105,77 @@ func versionCmd() *cobra.Command {
74105
}
75106
}
76107

77-
func updateCmd() *cobra.Command {
78-
return &cobra.Command{
79-
Use: "update",
80-
Short: "Check for updates and install if available",
81-
RunE: func(cmd *cobra.Command, args []string) error {
82-
fmt.Println("Update check not implemented yet")
83-
return nil
84-
},
108+
// Config holds the runtime configuration
109+
type Config struct {
110+
Token string
111+
URL string
112+
WorkspaceRoot string
113+
LogLevel string
114+
}
115+
116+
func loadConfig() (*Config, error) {
117+
cfg := &Config{}
118+
119+
// Token: flag > env
120+
cfg.Token = flagToken
121+
if cfg.Token == "" {
122+
cfg.Token = os.Getenv("FLASHDUTY_RUNNER_TOKEN")
123+
}
124+
if cfg.Token == "" {
125+
return nil, fmt.Errorf("token is required: use --token flag or set FLASHDUTY_RUNNER_TOKEN environment variable")
126+
}
127+
128+
// URL: flag > env > default
129+
cfg.URL = flagURL
130+
if cfg.URL == "" {
131+
cfg.URL = os.Getenv("FLASHDUTY_RUNNER_URL")
85132
}
133+
if cfg.URL == "" {
134+
cfg.URL = defaultURL
135+
}
136+
137+
// Workspace: flag > env > default
138+
cfg.WorkspaceRoot = flagWorkspace
139+
if cfg.WorkspaceRoot == "" {
140+
cfg.WorkspaceRoot = os.Getenv("FLASHDUTY_RUNNER_WORKSPACE")
141+
}
142+
if cfg.WorkspaceRoot == "" {
143+
homeDir, err := os.UserHomeDir()
144+
if err != nil {
145+
return nil, fmt.Errorf("failed to get home directory: %w", err)
146+
}
147+
cfg.WorkspaceRoot = filepath.Join(homeDir, ".flashduty-runner", "workspace")
148+
}
149+
150+
// Log level: flag > env > default
151+
cfg.LogLevel = flagLogLevel
152+
if cfg.LogLevel == "" {
153+
cfg.LogLevel = os.Getenv("FLASHDUTY_RUNNER_LOG_LEVEL")
154+
}
155+
if cfg.LogLevel == "" {
156+
cfg.LogLevel = defaultLogLevel
157+
}
158+
159+
return cfg, nil
86160
}
87161

88162
func runRunner() error {
89163
// Load configuration
90-
cfg, err := config.Load(configPath)
164+
cfg, err := loadConfig()
91165
if err != nil {
92-
return fmt.Errorf("failed to load config: %w", err)
166+
return err
93167
}
94168

95169
// Setup logging
96-
setupLogging(cfg.Log)
170+
setupLogging(cfg.LogLevel)
97171

98172
slog.Info("starting flashduty-runner",
99173
"version", Version,
100-
"name", cfg.Name,
101-
"labels", cfg.Labels,
174+
"workspace", cfg.WorkspaceRoot,
102175
)
103176

104-
// Create permission checker
105-
checker := permission.NewChecker(cfg.Permission.Bash)
177+
// Create permission checker with default deny-all policy
178+
checker := permission.NewChecker(map[string]string{"*": "deny"})
106179

107180
// Create workspace
108181
wspace, err := workspace.New(cfg.WorkspaceRoot, checker)
@@ -118,7 +191,7 @@ func runRunner() error {
118191
handler := ws.NewHandler(wspace)
119192

120193
// Create WebSocket client
121-
client := ws.NewClient(cfg, handler.Handle, Version)
194+
client := ws.NewClient(cfg.Token, cfg.URL, cfg.WorkspaceRoot, handler.Handle, Version)
122195
handler.SetClient(client)
123196

124197
// Setup signal handling
@@ -168,17 +241,10 @@ func runRunner() error {
168241
return nil
169242
}
170243

171-
func setupLogging(logCfg config.LogConfig) {
172-
level := parseLogLevel(logCfg.Level)
244+
func setupLogging(levelStr string) {
245+
level := parseLogLevel(levelStr)
173246
opts := &slog.HandlerOptions{Level: level}
174-
175-
var handler slog.Handler
176-
if logCfg.Format == "json" {
177-
handler = slog.NewJSONHandler(os.Stdout, opts)
178-
} else {
179-
handler = slog.NewTextHandler(os.Stdout, opts)
180-
}
181-
247+
handler := slog.NewTextHandler(os.Stdout, opts)
182248
slog.SetDefault(slog.New(handler))
183249
}
184250

config/config.go

Lines changed: 0 additions & 175 deletions
This file was deleted.

0 commit comments

Comments
 (0)