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=