diff --git a/cmd/wsh/cmd/html.go b/cmd/wsh/cmd/html.go new file mode 100644 index 000000000..1fde55b42 --- /dev/null +++ b/cmd/wsh/cmd/html.go @@ -0,0 +1,41 @@ +// Copyright 2024, Command Line Inc. +// SPDX-License-Identifier: Apache-2.0 + +package cmd + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" +) + +func init() { + rootCmd.AddCommand(htmlCmd) +} + +func htmlRun(cmd *cobra.Command, args []string) { + defer doShutdown("normal exit", 0) + setTermHtmlMode() + for { + var buf [1]byte + _, err := os.Stdin.Read(buf[:]) + if err != nil { + doShutdown(fmt.Sprintf("stdin closed/error (%v)", err), 1) + } + if buf[0] == 0x03 { + doShutdown("read Ctrl-C from stdin", 1) + break + } + if buf[0] == 'x' { + doShutdown("read 'x' from stdin", 0) + break + } + } +} + +var htmlCmd = &cobra.Command{ + Use: "html", + Short: "Launch a demo html-mode terminal", + Run: htmlRun, +} diff --git a/cmd/wsh/cmd/root.go b/cmd/wsh/cmd/root.go new file mode 100644 index 000000000..3441a9f65 --- /dev/null +++ b/cmd/wsh/cmd/root.go @@ -0,0 +1,84 @@ +// Copyright 2024, Command Line Inc. +// SPDX-License-Identifier: Apache-2.0 + +package cmd + +import ( + "fmt" + "log" + "os" + "os/signal" + "sync" + "syscall" + + "github.com/spf13/cobra" + "github.com/wavetermdev/thenextwave/pkg/wshutil" + "golang.org/x/term" +) + +var ( + rootCmd = &cobra.Command{ + Use: "wsh", + Short: "CLI tool to control Wave Terminal", + Long: `wsh is a small utility that lets you do cool things with Wave Terminal, right from the command line`, + } +) + +var shutdownOnce sync.Once +var origTermState *term.State +var usingHtmlMode bool +var shutdownSignalHandlersInstalled bool + +func doShutdown(reason string, exitCode int) { + shutdownOnce.Do(func() { + defer os.Exit(exitCode) + log.Printf("shutting down: %s\r\n", reason) + if usingHtmlMode { + cmd := &wshutil.BlockSetMetaCommand{ + Command: wshutil.BlockCommand_SetMeta, + Meta: map[string]any{"term:mode": nil}, + } + barr, _ := wshutil.EncodeWaveOSCMessage(cmd) + os.Stdout.Write(barr) + } + if origTermState != nil { + term.Restore(int(os.Stdin.Fd()), origTermState) + } + }) +} + +func setTermHtmlMode() { + installShutdownSignalHandlers() + origState, err := term.MakeRaw(int(os.Stdin.Fd())) + if err != nil { + fmt.Fprintf(os.Stderr, "Error setting raw mode: %v\n", err) + return + } + origTermState = origState + cmd := &wshutil.BlockSetMetaCommand{ + Command: wshutil.BlockCommand_SetMeta, + Meta: map[string]any{"term:mode": "html"}, + } + barr, _ := wshutil.EncodeWaveOSCMessage(cmd) + os.Stdout.Write(barr) + usingHtmlMode = true +} + +func installShutdownSignalHandlers() { + if shutdownSignalHandlersInstalled { + return + } + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGINT) + go func() { + for sig := range sigCh { + doShutdown(fmt.Sprintf("got signal %v", sig), 1) + break + } + }() +} + +// Execute executes the root command. +func Execute() error { + return rootCmd.Execute() +} diff --git a/cmd/wsh/cmd/version.go b/cmd/wsh/cmd/version.go new file mode 100644 index 000000000..44784b20b --- /dev/null +++ b/cmd/wsh/cmd/version.go @@ -0,0 +1,22 @@ +// Copyright 2024, Command Line Inc. +// SPDX-License-Identifier: Apache-2.0 + +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +func init() { + rootCmd.AddCommand(versionCmd) +} + +var versionCmd = &cobra.Command{ + Use: "version", + Short: "Print the version number of wsh", + Run: func(cmd *cobra.Command, args []string) { + fmt.Printf("wsh v0.1.0\n") + }, +} diff --git a/cmd/wsh/main-wsh.go b/cmd/wsh/main-wsh.go index a8bb8c4f1..12419fe10 100644 --- a/cmd/wsh/main-wsh.go +++ b/cmd/wsh/main-wsh.go @@ -4,75 +4,10 @@ package main import ( - "fmt" - "log" - "os" - "os/signal" - "sync" - "syscall" - - "github.com/wavetermdev/thenextwave/pkg/wshutil" - "golang.org/x/term" + "github.com/wavetermdev/thenextwave/cmd/wsh/cmd" ) -var shutdownOnce sync.Once -var origTermState *term.State - -func doShutdown(reason string, exitCode int) { - shutdownOnce.Do(func() { - defer os.Exit(exitCode) - log.Printf("shutting down: %s\r\n", reason) - cmd := &wshutil.BlockSetMetaCommand{ - Command: wshutil.BlockCommand_SetMeta, - Meta: map[string]any{"term:mode": nil}, - } - barr, _ := wshutil.EncodeWaveOSCMessage(cmd) - if origTermState != nil { - term.Restore(int(os.Stdin.Fd()), origTermState) - } - os.Stdout.Write(barr) - }) -} - -func installShutdownSignalHandlers() { - sigCh := make(chan os.Signal, 1) - signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGINT) - go func() { - for sig := range sigCh { - doShutdown(fmt.Sprintf("got signal %v", sig), 1) - break - } - }() -} - func main() { - installShutdownSignalHandlers() - defer doShutdown("normal exit", 0) - origState, err := term.MakeRaw(int(os.Stdin.Fd())) - if err != nil { - fmt.Fprintf(os.Stderr, "Error setting raw mode: %v\n", err) - return - } - origTermState = origState - cmd := &wshutil.BlockSetMetaCommand{ - Command: wshutil.BlockCommand_SetMeta, - Meta: map[string]any{"term:mode": "html"}, - } - barr, _ := wshutil.EncodeWaveOSCMessage(cmd) - os.Stdout.Write(barr) - for { - var buf [1]byte - _, err := os.Stdin.Read(buf[:]) - if err != nil { - doShutdown(fmt.Sprintf("stdin closed/error (%v)", err), 1) - } - if buf[0] == 0x03 { - doShutdown("read Ctrl-C from stdin", 1) - break - } - if buf[0] == 'x' { - doShutdown("read 'x' from stdin", 0) - break - } - } + cmd.Execute() + } diff --git a/go.mod b/go.mod index 6501a537b..990843d41 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( github.com/mattn/go-sqlite3 v1.14.22 github.com/mitchellh/mapstructure v1.5.0 github.com/sawka/txwrap v0.2.0 + github.com/spf13/cobra v1.8.0 github.com/wavetermdev/waveterm/wavesrv v0.0.0-20240508181017-d07068c09d94 golang.org/x/sys v0.20.0 golang.org/x/term v0.17.0 @@ -24,6 +25,8 @@ require ( github.com/felixge/httpsnoop v1.0.3 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/testify v1.8.4 // indirect go.uber.org/atomic v1.7.0 // indirect ) diff --git a/go.sum b/go.sum index 4b6ea66ac..7f1f1f438 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,6 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -24,6 +25,8 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= @@ -34,8 +37,13 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sawka/txwrap v0.2.0 h1:V3LfvKVLULxcYSxdMguLwFyQFMEU9nFDJopg0ZkL+94= github.com/sawka/txwrap v0.2.0/go.mod h1:wwQ2SQiN4U+6DU/iVPhbvr7OzXAtgZlQCIGuvOswEfA= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= @@ -48,5 +56,6 @@ golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=