@@ -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"
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
3042func 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
5462func 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
6596func 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
88162func 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
0 commit comments