diff --git a/cmd/cli/main.go b/cmd/cli/main.go index e48d891..ad4ab7f 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -20,9 +20,10 @@ import ( ) var ( - cfg *config.Config - prompt string - conversationID string + cfg *config.Config + startPrompt string + interactiveMode bool + startConversationId string ) const ( @@ -34,9 +35,9 @@ var rootCmd = &cobra.Command{ Short: "An enhanced AI runtime, focusing on ease of use and extensibility.", Run: func(cmd *cobra.Command, args []string) { selectedPrompt := &prompts.DefaultPrompt - if prompt != "" { + if startPrompt != "" { var err error - selectedPrompt, err = prompts.LoadPrompt(prompt) + selectedPrompt, err = prompts.LoadPrompt(startPrompt) if err != nil { fmt.Printf("Error loading prompt: %v\n", err) os.Exit(1) @@ -122,9 +123,12 @@ func main() { // #endregion // Attach flags to rootCmd only, so they are not inherited by subcommands - rootCmd.Flags().StringVarP(&prompt, "prompt", "p", "", "Specify a prompt") rootCmd.Flags(). - StringVarP(&conversationID, "conversation", "c", "", "Specify a conversation ID") + StringVarP(&startPrompt, "prompt", "p", "", "Specify a prompt") + rootCmd.Flags(). + StringVarP(&startConversationId, "conversation", "c", "", "Open a conversation by ID") + rootCmd.Flags(). + BoolVarP(&interactiveMode, "interactive", "i", false, "Start in interactive mode") // Initialize cfg in PersistentPreRun, making it available to all commands rootCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) { diff --git a/cmd/ui/main.go b/cmd/ui/main.go new file mode 100644 index 0000000..ad1a8ed --- /dev/null +++ b/cmd/ui/main.go @@ -0,0 +1,282 @@ +package main + +import ( + "log" + + "github.com/gdamore/tcell/v2" + "github.com/rivo/tview" +) + +// TODO: Multi-line input +// TODO: Copy text from text view, only, not conversation + +const longText = ` +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam consectetur ornare lobortis. Donec a lectus vitae enim sollicitudin fermentum. Quisque fringilla sapien vitae arcu volutpat, in lacinia augue iaculis. Aenean ac rutrum elit. In hac habitasse platea dictumst. Donec pellentesque justo porttitor tincidunt volutpat. Morbi faucibus, erat eu porta feugiat, tellus orci rutrum lectus, a maximus velit lectus interdum odio. Proin urna augue, egestas ut lectus sed, fringilla lacinia turpis. Integer porttitor quam vel felis ultricies euismod. Vivamus elementum mauris eu aliquam consequat. Nullam iaculis aliquet purus, nec mollis nisl accumsan non. + +Maecenas ac suscipit felis. Nulla non justo felis. Pellentesque euismod magna vitae molestie tempus. Integer dictum enim sit amet scelerisque fringilla. Nam auctor justo nec odio faucibus, eget consequat orci tempor. Mauris efficitur arcu non risus laoreet commodo. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vestibulum nisl tellus, molestie eget nisl sit amet, auctor tristique libero. Nulla magna tellus, ultricies sed consectetur quis, convallis quis orci. Sed elementum dictum sem, ut eleifend massa convallis eu. Etiam vitae nisl id mi malesuada fringilla nec in purus. Sed mi est, pulvinar non augue quis, convallis cursus ligula. Etiam egestas eros quis pharetra ullamcorper. Mauris consequat auctor aliquam. Ut luctus vel eros at tempus. Fusce a neque at arcu tincidunt vehicula non nec metus. + +Nulla suscipit aliquet lorem quis cursus. Praesent convallis eleifend risus, eget cursus sapien aliquam sit amet. Quisque consequat felis sem, nec ultrices justo gravida non. Vestibulum in mauris a felis pellentesque placerat ut et enim. Nunc ut velit sed mi porttitor maximus sit amet non urna. Donec eu odio nunc. Aenean faucibus lobortis erat, in tempus ligula tempor ac. Integer iaculis tincidunt augue, vitae egestas ante fermentum a. Aliquam commodo ut ipsum eget posuere. Duis sit amet sem et neque vulputate malesuada. Donec ornare diam risus, nec ultricies leo finibus in. Nunc orci sapien, auctor et nisi ac, volutpat aliquam tellus. Maecenas venenatis venenatis libero, id aliquam tellus hendrerit et. Nulla ac nibh mattis, tempus nisl ut, lobortis lorem. Sed sit amet interdum nisi. + +Etiam scelerisque justo sit amet urna vestibulum, sit amet vulputate mauris mattis. Etiam dolor justo, faucibus in elementum ut, fermentum at est. Proin nisl nibh, interdum ac eros eu, venenatis cursus arcu. Morbi semper ornare augue, at faucibus ex aliquet ac. Donec nec venenatis eros. Pellentesque aliquet fringilla lorem vitae sollicitudin. Aenean porttitor, diam vel gravida tincidunt, nibh lectus venenatis elit, sed posuere velit mauris eget erat. + +Nunc accumsan condimentum turpis, in ullamcorper dui finibus non. Mauris feugiat metus ut leo blandit consectetur sed eu metus. Donec mi arcu, ultricies in ultrices ac, malesuada in nulla. Aliquam sagittis, ante ac bibendum tristique, erat arcu auctor eros, eu accumsan neque dolor sit amet quam. Donec scelerisque turpis nunc, at vulputate lorem dignissim sed. Aenean gravida, ligula a sodales laoreet, ante quam malesuada diam, ut feugiat quam velit nec sapien. Donec sem erat, auctor non eros ut, porta dignissim sapien. Integer felis lectus, molestie ut dui et, iaculis sollicitudin turpis. Sed auctor.` + +type UI struct { + app *tview.Application + + conversations map[string]string + currentConversation string + + table *tview.Table + textView *tview.TextView + input *tview.TextArea + flex *tview.Flex +} + +func NewUI() *UI { + ui := &UI{ + app: tview.NewApplication(), + conversations: make(map[string]string), + currentConversation: "New Conversation", + } + + ui.conversations["New Conversation"] = "" + ui.conversations["Conversation 1"] = "" + ui.conversations["Conversation 2"] = "" + + ui.setupTable() + ui.setupTextView() + ui.setupInputField() + ui.setupLayout() + ui.setupEventHandlers() + + return ui +} + +func (c *UI) setupTable() { + c.table = tview.NewTable() + c.table.SetBorders(false) + c.table.SetSelectable(true, false) + c.table.SetBackgroundColor(tcell.ColorDefault) + + row := 0 + for conv := range c.conversations { + cell := tview.NewTableCell(conv). + SetSelectable(true). + SetTextColor(tcell.ColorWhite). + SetBackgroundColor(tcell.ColorDefault) + c.table.SetCell(row, 0, cell) + row++ + } + + c.table.Select(0, 0) +} + +func (c *UI) setupTextView() { + c.textView = tview.NewTextView() + c.textView.SetBackgroundColor(tcell.ColorDefault) + c.textView.SetBorder(false) + c.updateTextView() +} + +func (c *UI) setupInputField() { + // TODO(nullswan): Make cursor blink + c.input = tview.NewTextArea() + c.input.SetBackgroundColor(tcell.ColorDefault) + c.input.SetPlaceholder("Type your message here...") + c.input.SetPlaceholderStyle(tcell.StyleDefault.Foreground(tcell.ColorGray)) + c.input.SetTextStyle(tcell.StyleDefault.Foreground(tcell.ColorWhite)) + c.input.SetBorder(false) + + c.input.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { + if event.Key() == tcell.KeyEnter { + modifiers := event.Modifiers() + if modifiers&tcell.ModShift != 0 || modifiers&tcell.ModCtrl != 0 || + modifiers&tcell.ModMeta != 0 { + // Insert a newline + c.input.SetText(c.input.GetText()+"\n", true) + return nil + } else { + // Submit the message + inputText := c.input.GetText() + if inputText != "" { + if c.conversations[c.currentConversation] == "" { + c.conversations[c.currentConversation] = inputText + } else { + c.conversations[c.currentConversation] += "\n" + inputText + } + c.updateTextView() + c.input.SetText("", true) + // c.textArea.ScrollToEnd() + } + return nil + } + } + return event + }) +} + +func (c *UI) setupLayout() { + // Create a horizontal divider for the left panel + dividerHorizontalLeft := tview.NewBox() + dividerHorizontalLeft.SetDrawFunc( + func(screen tcell.Screen, x, y, width, height int) (int, int, int, int) { + style := tcell.StyleDefault. + Foreground(tcell.ColorGrey). + Background(tcell.ColorDefault). + Bold(true) + + // Draw a thick horizontal line + for i := x; i < x+width; i++ { + screen.SetContent(i, y, tcell.RuneHLine, nil, style) + screen.SetContent(i, y+1, tcell.RuneHLine, nil, style) + } + return x, y, width, height + }, + ) + + // Create a text box below the conversation list + conversationTextBox := tview.NewTextView() + conversationTextBox.SetText("{Provider}\n{Delay}") + conversationTextBox.SetBackgroundColor(tcell.ColorDefault) + + // Update the left panel layout + leftFlex := tview.NewFlex() + leftFlex.SetDirection(tview.FlexRow) + leftFlex.AddItem(c.table, 0, 1, true) + leftFlex.AddItem(dividerHorizontalLeft, 1, 0, false) + leftFlex.AddItem(conversationTextBox, 3, 0, false) + + // Create a horizontal divider + dividerHorizontal := tview.NewBox() + dividerHorizontal.SetDrawFunc( + func(screen tcell.Screen, x, y, width, height int) (int, int, int, int) { + style := tcell.StyleDefault. + Foreground(tcell.ColorGrey). + Background(tcell.ColorDefault). + Bold(true) + + // Draw a thick horizontal line + for i := x; i < x+width; i++ { + screen.SetContent(i, y, tcell.RuneHLine, nil, style) + screen.SetContent(i, y+1, tcell.RuneHLine, nil, style) + } + return x, y, width, height + }, + ) + + flexMain := tview.NewFlex() + flexMain.SetDirection(tview.FlexRow) + flexMain.AddItem(c.textView, 0, 1, false) + flexMain.AddItem(dividerHorizontal, 1, 0, false) + flexMain.AddItem(c.input, 5, 0, true) + flexMain.SetBackgroundColor(tcell.ColorDefault) + + // Create a vertical divider + divider := tview.NewBox() + divider.SetDrawFunc( + func(screen tcell.Screen, x, y, width, height int) (int, int, int, int) { + style := tcell.StyleDefault. + Foreground(tcell.ColorGrey). + Background(tcell.ColorDefault). + Bold(true) + + // Draw a thick vertical line + for i := y; i < y+height; i++ { + screen.SetContent(x, i, tcell.RuneVLine, nil, style) + screen.SetContent(x+1, i, tcell.RuneVLine, nil, style) + } + return x, y, width, height + }, + ) + + c.flex = tview.NewFlex() + c.flex.AddItem(leftFlex, 20, 1, true) + c.flex.AddItem(divider, 1, 0, false) + c.flex.AddItem(flexMain, 0, 5, false) + c.flex.SetBackgroundColor(tcell.ColorDefault) +} + +func (c *UI) setupEventHandlers() { + tableSelectedFunc := func(row, column int) { + cell := c.table.GetCell(row, column) + if cell == nil { + return + } + selected := cell.Text + if selected != "" { + c.currentConversation = selected + c.updateTextView() + c.app.SetFocus(c.input) + } + } + c.table.SetSelectedFunc(tableSelectedFunc) + + c.table.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { + if event.Key() == tcell.KeyEnter { + row, column := c.table.GetSelection() + tableSelectedFunc(row, column) + return nil + } + return event + }) + + c.app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { + if event.Key() == tcell.KeyTab { + switch c.app.GetFocus() { + case c.input: + c.app.SetFocus(c.textView) + case c.textView: + c.app.SetFocus(c.table) + case c.table: + c.app.SetFocus(c.input) + } + return nil + } + return event + }) + + // c.textView.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { + // switch event.Key() { + // case tcell.KeyUp: + // c.textView.ScrollUp() + // return nil + // case tcell.KeyDown: + // c.textView.ScrollDown() + // return nil + // case tcell.KeyPgUp: + // c.textView.ScrollPageUp() + // return nil + // case tcell.KeyPgDn: + // c.textView.ScrollPageDown() + // return nil + // } + // return event + // }) +} + +func (c *UI) updateTextView() { + content := c.conversations[c.currentConversation] + if content == "" { + content = "## " + c.currentConversation + "\n\n_Start your conversation..._" + } + + content += longText + longText // Duplicate to make it longer + c.textView.SetText(content) + c.textView.ScrollToEnd() +} + +func main() { + ui := NewUI() + + ui.app.SetRoot(ui.flex, true) + ui.app.EnablePaste(true) + ui.app.EnableMouse(true) + + // Start with input field focused + ui.app.SetFocus(ui.input) + + if err := ui.app.Run(); err != nil { + log.Fatalf("Error running application: %v", err) + } +} diff --git a/go.mod b/go.mod index fd1a47c..b05305e 100644 --- a/go.mod +++ b/go.mod @@ -27,6 +27,9 @@ require ( github.com/chzyer/readline v1.5.1 // indirect github.com/dlclark/regexp2 v1.11.4 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect + github.com/gdamore/encoding v1.0.1 // indirect + github.com/gdamore/tcell v1.4.0 // indirect + github.com/gdamore/tcell/v2 v2.7.4 // indirect github.com/gorilla/css v1.0.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect @@ -38,6 +41,7 @@ require ( github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a // indirect + github.com/rivo/tview v0.0.0-20240921122403-a64fc48d7654 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/yuin/goldmark v1.7.4 // indirect diff --git a/go.sum b/go.sum index 5b5ce84..e057943 100644 --- a/go.sum +++ b/go.sum @@ -44,6 +44,13 @@ github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yA github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= +github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= +github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw= +github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo= +github.com/gdamore/tcell v1.4.0 h1:vUnHwJRvcPQa3tzi+0QI4U9JINXYJlOz9yiaiPQ2wMU= +github.com/gdamore/tcell v1.4.0/go.mod h1:vxEiSDZdW3L+Uhjii9c3375IlDmR05bzxY404ZVSMo0= +github.com/gdamore/tcell/v2 v2.7.4 h1:sg6/UnTM9jGpZU+oFYAsDahfchWAFW8Xx2yFinNSAYU= +github.com/gdamore/tcell/v2 v2.7.4/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7ONmsraWg= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gordonklaus/portaudio v0.0.0-20230709114228-aafa478834f5 h1:5AlozfqaVjGYGhms2OsdUyfdJME76E6rx5MdGpjzZpc= @@ -56,6 +63,7 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jedib0t/go-pretty/v6 v6.5.9 h1:ACteMBRrrmm1gMsXe9PSTOClQ63IXDUt03H5U+UV8OU= github.com/jedib0t/go-pretty/v6 v6.5.9/go.mod h1:zbn98qrYlh95FIhwwsbIip0LYpwSG8SUOScs+v9/t0E= +github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= @@ -64,7 +72,9 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/maxhawkins/go-webrtcvad v0.0.0-20210121163624-be60036f3083 h1:0JDcvP4R28p6+u8VIHCwYx7UwiHZ074INz3C397oc9s= @@ -83,8 +93,11 @@ github.com/ollama/ollama v0.3.12 h1:u7KFCKNDOEDD3kLlziB9XGeK6Jb1sLmlkrJSe8d5fS0= github.com/ollama/ollama v0.3.12/go.mod h1:YrWoNkFnPOYsnDvsf/Ztb1wxU9/IXrNsQHqcxbY2r94= 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/rivo/tview v0.0.0-20240921122403-a64fc48d7654 h1:oa+fljZiaJUVyiT7WgIM3OhirtwBm0LJA97LvWUlBu8= +github.com/rivo/tview v0.0.0-20240921122403-a64fc48d7654/go.mod h1:02iFIz7K/A9jGCvrizLPvoqr4cEIx7q54RH5Qudkrss= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -96,25 +109,59 @@ 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/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg= github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark-emoji v1.0.3 h1:aLRkLHOuBR2czCY4R8olwMjID+tENfhyFDMCRhbIQY4= github.com/yuin/goldmark-emoji v1.0.3/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=