@@ -26,28 +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- )
36- flag .Usage = func () {
37- fmt .Fprintf (os .Stderr , "Usage: bodek [options] [-- <odek serve flags>]\n \n " )
38- fmt .Fprintf (os .Stderr , "A terminal interface for the odek agent.\n \n " )
39- fmt .Fprintf (os .Stderr , "Options:\n " )
40- flag .PrintDefaults ()
41- fmt .Fprintf (os .Stderr , "\n Examples:\n " )
42- fmt .Fprintf (os .Stderr , " bodek # spawn odek serve and start chatting\n " )
43- fmt .Fprintf (os .Stderr , " bodek --sandbox # spawn odek serve with Docker sandbox\n " )
44- fmt .Fprintf (os .Stderr , " bodek --url 'http://127.0.0.1:8080/?token=…' # attach with the token URL odek serve printed\n " )
45- fmt .Fprintf (os .Stderr , " bodek --url http://127.0.0.1:8080 --token d3adb33f # attach with an explicit token\n " )
46- 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 " )
4761 }
48- flag .Parse ()
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+ }
4969
50- extraArgs := flag .Args ()
70+ func buildProgramOptions (mouse bool ) []tea.ProgramOption {
71+ opts := []tea.ProgramOption {tea .WithAltScreen ()}
72+ if mouse {
73+ opts = append (opts , tea .WithMouseCellMotion ())
74+ }
75+ return opts
76+ }
77+
78+ func run () error {
79+ cfg , err := parseConfig (os .Args [1 :], os .Stderr )
80+ if err != nil {
81+ return err
82+ }
5183
5284 // A spawned `odek serve` logs to stderr. Routing that to our own terminal
5385 // would corrupt the Bubble Tea alt-screen (stray writes desync the diff
@@ -62,7 +94,7 @@ func run() error {
6294 // io.MultiWriter / *ringWriter below) without the redundant typed-var
6395 // declaration that staticcheck's QF1011 flags.
6496 serverErr := io .Writer (io .Discard )
65- if * url == "" {
97+ if cfg . url == "" {
6698 logTail = newRingWriter (50 )
6799 if f , path , closeLog := openServerLog (); path != "" {
68100 defer closeLog ()
@@ -75,11 +107,11 @@ func run() error {
75107
76108 // Spawn or attach to the odek serve backend.
77109 srv , err := server .Connect (server.Options {
78- URL : * url ,
79- Token : * token ,
80- Bin : * bin ,
81- Sandbox : * sandbox ,
82- ExtraArgs : extraArgs ,
110+ URL : cfg . url ,
111+ Token : cfg . token ,
112+ Bin : cfg . bin ,
113+ Sandbox : cfg . sandbox ,
114+ ExtraArgs : cfg . extraArgs ,
83115 Stderr : serverErr ,
84116 })
85117 if err != nil {
@@ -108,16 +140,16 @@ func run() error {
108140 setupSignalHandler (srv , cl )
109141
110142 model := tui .New (cl , tui.Options {
111- Sandbox : * sandbox ,
143+ Sandbox : cfg . sandbox ,
112144 CWD : cwd ,
113145 LogPath : logPath ,
114146 })
115147
116- // Mouse reporting enables wheel scrolling in the transcript. Click-drag text
117- // selection is delegated to the terminal's shift+ drag fallback where the
118- // terminal supports it; otherwise users can rely on keyboard scrolling
119- // (↑/↓, PgUp/PgDn, ^U/^D) .
120- p := tea .NewProgram (model , tea . WithAltScreen (), tea . WithMouseCellMotion () )
148+ // Mouse reporting enables wheel scrolling in the transcript, but it also
149+ // captures the terminal mouse and blocks native click- drag text selection
150+ // and copy. Keep it off by default so users can copy freely; enable it only
151+ // when explicitly requested with --mouse .
152+ p := tea .NewProgram (model , buildProgramOptions ( cfg . mouse ) ... )
121153 if _ , err := p .Run (); err != nil {
122154 return fmt .Errorf ("TUI exited: %w" , err )
123155 }
0 commit comments