@@ -26,29 +26,60 @@ func main() {
2626 }
2727}
2828
29- func run () error {
30- var (
31- url = flag .String ("url" , "" , "attach to an already-running odek serve URL (e.g. http://127.0.0.1:8080/?token=…)" )
32- token = flag .String ("token" , "" , "WS auth token for an attached odek serve (as printed at its startup)" )
33- sandbox = flag .Bool ("sandbox" , false , "run tool calls inside odek's Docker sandbox" )
34- bin = flag .String ("odek-bin" , "" , "path to the odek binary to spawn (default: odek on PATH)" )
35- mouse = flag .Bool ("mouse" , false , "enable mouse wheel scrolling (disables native text selection/copy)" )
36- )
37- flag .Usage = func () {
38- fmt .Fprintf (os .Stderr , "Usage: bodek [options] [-- <odek serve flags>]\n \n " )
39- fmt .Fprintf (os .Stderr , "A terminal interface for the odek agent.\n \n " )
40- fmt .Fprintf (os .Stderr , "Options:\n " )
41- flag .PrintDefaults ()
42- fmt .Fprintf (os .Stderr , "\n Examples:\n " )
43- fmt .Fprintf (os .Stderr , " bodek # spawn odek serve and start chatting\n " )
44- fmt .Fprintf (os .Stderr , " bodek --sandbox # spawn odek serve with Docker sandbox\n " )
45- fmt .Fprintf (os .Stderr , " bodek --url 'http://127.0.0.1:8080/?token=…' # attach with the token URL odek serve printed\n " )
46- fmt .Fprintf (os .Stderr , " bodek --url http://127.0.0.1:8080 --token d3adb33f # attach with an explicit token\n " )
47- fmt .Fprintf (os .Stderr , " bodek -- --prompt-caching # pass extra flags to odek serve\n " )
29+ type config struct {
30+ url string
31+ token string
32+ sandbox bool
33+ bin string
34+ mouse bool
35+ extraArgs []string
36+ }
37+
38+ func parseConfig (args []string , output io.Writer ) (config , error ) {
39+ var cfg config
40+ fs := flag .NewFlagSet ("bodek" , flag .ContinueOnError )
41+ if output != nil {
42+ fs .SetOutput (output )
43+ }
44+ fs .StringVar (& cfg .url , "url" , "" , "attach to an already-running odek serve URL (e.g. http://127.0.0.1:8080/?token=…)" )
45+ fs .StringVar (& cfg .token , "token" , "" , "WS auth token for an attached odek serve (as printed at its startup)" )
46+ fs .BoolVar (& cfg .sandbox , "sandbox" , false , "run tool calls inside odek's Docker sandbox" )
47+ fs .StringVar (& cfg .bin , "odek-bin" , "" , "path to the odek binary to spawn (default: odek on PATH)" )
48+ fs .BoolVar (& cfg .mouse , "mouse" , false , "enable mouse wheel scrolling (disables native text selection/copy)" )
49+ fs .Usage = func () {
50+ fmt .Fprintf (fs .Output (), "Usage: bodek [options] [-- <odek serve flags>]\n \n " )
51+ fmt .Fprintf (fs .Output (), "A terminal interface for the odek agent.\n \n " )
52+ fmt .Fprintf (fs .Output (), "Options:\n " )
53+ fs .PrintDefaults ()
54+ fmt .Fprintf (fs .Output (), "\n Examples:\n " )
55+ fmt .Fprintf (fs .Output (), " bodek # spawn odek serve and start chatting\n " )
56+ fmt .Fprintf (fs .Output (), " bodek --sandbox # spawn odek serve with Docker sandbox\n " )
57+ fmt .Fprintf (fs .Output (), " bodek --url 'http://127.0.0.1:8080/?token=…' # attach with the token URL odek serve printed\n " )
58+ fmt .Fprintf (fs .Output (), " bodek --url http://127.0.0.1:8080 --token d3adb33f # attach with an explicit token\n " )
59+ fmt .Fprintf (fs .Output (), " bodek --mouse # enable mouse wheel scrolling (blocks text selection)\n " )
60+ fmt .Fprintf (fs .Output (), " bodek -- --prompt-caching # pass extra flags to odek serve\n " )
61+ }
62+ if err := fs .Parse (args ); err != nil {
63+ fs .Usage ()
64+ return config {}, err
65+ }
66+ cfg .extraArgs = fs .Args ()
67+ return cfg , nil
68+ }
69+
70+ func buildProgramOptions (mouse bool ) []tea.ProgramOption {
71+ opts := []tea.ProgramOption {tea .WithAltScreen ()}
72+ if mouse {
73+ opts = append (opts , tea .WithMouseCellMotion ())
4874 }
49- flag .Parse ()
75+ return opts
76+ }
5077
51- extraArgs := flag .Args ()
78+ func run () error {
79+ cfg , err := parseConfig (os .Args [1 :], os .Stderr )
80+ if err != nil {
81+ return err
82+ }
5283
5384 // A spawned `odek serve` logs to stderr. Routing that to our own terminal
5485 // would corrupt the Bubble Tea alt-screen (stray writes desync the diff
@@ -63,7 +94,7 @@ func run() error {
6394 // io.MultiWriter / *ringWriter below) without the redundant typed-var
6495 // declaration that staticcheck's QF1011 flags.
6596 serverErr := io .Writer (io .Discard )
66- if * url == "" {
97+ if cfg . url == "" {
6798 logTail = newRingWriter (50 )
6899 if f , path , closeLog := openServerLog (); path != "" {
69100 defer closeLog ()
@@ -76,11 +107,11 @@ func run() error {
76107
77108 // Spawn or attach to the odek serve backend.
78109 srv , err := server .Connect (server.Options {
79- URL : * url ,
80- Token : * token ,
81- Bin : * bin ,
82- Sandbox : * sandbox ,
83- ExtraArgs : extraArgs ,
110+ URL : cfg . url ,
111+ Token : cfg . token ,
112+ Bin : cfg . bin ,
113+ Sandbox : cfg . sandbox ,
114+ ExtraArgs : cfg . extraArgs ,
84115 Stderr : serverErr ,
85116 })
86117 if err != nil {
@@ -109,7 +140,7 @@ func run() error {
109140 setupSignalHandler (srv , cl )
110141
111142 model := tui .New (cl , tui.Options {
112- Sandbox : * sandbox ,
143+ Sandbox : cfg . sandbox ,
113144 CWD : cwd ,
114145 LogPath : logPath ,
115146 })
@@ -118,11 +149,7 @@ func run() error {
118149 // captures the terminal mouse and blocks native click-drag text selection
119150 // and copy. Keep it off by default so users can copy freely; enable it only
120151 // when explicitly requested with --mouse.
121- programOpts := []tea.ProgramOption {tea .WithAltScreen ()}
122- if * mouse {
123- programOpts = append (programOpts , tea .WithMouseCellMotion ())
124- }
125- p := tea .NewProgram (model , programOpts ... )
152+ p := tea .NewProgram (model , buildProgramOptions (cfg .mouse )... )
126153 if _ , err := p .Run (); err != nil {
127154 return fmt .Errorf ("TUI exited: %w" , err )
128155 }
0 commit comments