Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ vendor/
.DS_Store
Thumbs.db

runix*
runix*
!setting/runix.local
29 changes: 29 additions & 0 deletions go/chat/streamer.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ import (
"github.com/fatih/color"
)

const asciiBanner = `____/\\\\\______/\\________/\\__/\\\_____/\\__/\\\\\\\\__/\\_______/\\_
__/\\///////\\___\/\\_______\/\\_\/\\\\\___\/\\_\/////\\///__\///\\___/\\\/__
_\/\\_____\/\\___\/\\_______\/\\_\/\\\/\\__\/\\_____\/\\_______\///\\\\\/____
_\/\\\\\\\\\/____\/\\_______\/\\_\/\\//\\_\/\\_____\/\\_________\//\\______
_\/\\//////\\____\/\\_______\/\\_\/\\\\//\\/\\_____\/\\__________\/\\______
_\/\\____\//\\___\/\\_______\/\\_\/\\_\//\\\/\\_____\/\\__________/\\\\\_____
_\/\\_____\//\\__\//\\______/\\__\/\\__\//\\\\\_____\/\\________/\\////\\___
_\/\\______\//\\__\///\\\\\\\/___\/\\___\//\\\__/\\\\\\\\\/__/\\\/___\///\\_
_\///________\///_____\////////_____\///_____\////__/\\\\\\///__\///_______\///__`

type ChatStreamer struct {
typingSpeed time.Duration
}
Expand All @@ -23,6 +33,9 @@ func NewChatStreamer() *ChatStreamer {

// StreamResponse streams the AI response in real-time with special handling for code blocks
func (cs *ChatStreamer) StreamResponse(role, response string) {
// Format response for better streaming
response = cs.FormatResponse(response)

// Colors for different roles
userColor := color.New(color.FgCyan, color.Bold)
botColor := color.New(color.FgGreen, color.Bold)
Expand Down Expand Up @@ -133,6 +146,13 @@ func (cs *ChatStreamer) showProgressBar(message string, steps int) {
fmt.Print("\n")
}

// FormatResponse normalizes whitespace and line endings
func (cs *ChatStreamer) FormatResponse(resp string) string {
resp = strings.ReplaceAll(resp, "\r\n", "\n")
resp = regexp.MustCompile(`\n{3,}`).ReplaceAllString(resp, "\n\n")
return strings.TrimSpace(resp)
}

// ResponsePart represents a part of the response (text or code)
type ResponsePart struct {
content string
Expand Down Expand Up @@ -213,6 +233,8 @@ func (cs *ChatStreamer) containsCodeBlocks(response string) bool {
"```js",
"```python",
"```go",
"```bash",
"```shell",
}

for _, pattern := range patterns {
Expand All @@ -225,6 +247,7 @@ func (cs *ChatStreamer) containsCodeBlocks(response string) bool {

// ShowWelcome shows the welcome message with style
func (cs *ChatStreamer) ShowWelcome() {
cs.ShowBanner()
titleColor := color.New(color.FgMagenta, color.Bold)
subtitleColor := color.New(color.FgCyan)
tipColor := color.New(color.FgYellow)
Expand Down Expand Up @@ -260,3 +283,9 @@ func (cs *ChatStreamer) ShowThinking() {

thinkingColor.Print("💭 Generando respuesta...\n")
}

// ShowBanner prints the Runix ASCII art banner
func (cs *ChatStreamer) ShowBanner() {
bannerColor := color.New(color.FgHiMagenta, color.Bold)
bannerColor.Println(asciiBanner)
}
23 changes: 19 additions & 4 deletions go/cli/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@ import (
"runix/go/filemanager"
"runix/go/openrouter"
"runix/go/processor"
"runix/go/settings"
"runix/go/webserver"
)

var (
globalFileManager *filemanager.FileManager
globalWebServer *webserver.Server
globalProcessor *processor.ResponseProcessor
globalStreamer *chat.ChatStreamer
globalFileManager *filemanager.FileManager
globalWebServer *webserver.Server
globalConfigServer *webserver.ConfigServer
globalProcessor *processor.ResponseProcessor
globalStreamer *chat.ChatStreamer
)

// Execute parses CLI arguments and dispatches to subcommands.
Expand Down Expand Up @@ -60,6 +62,9 @@ func usage() {
func initializeSystem() error {
var err error

// Load local environment variables
_ = settings.LoadEnv("setting/runix.local")

// Initialize file manager
globalFileManager, err = filemanager.NewFileManager()
if err != nil {
Expand All @@ -75,6 +80,10 @@ func initializeSystem() error {
// Initialize chat streamer
globalStreamer = chat.NewChatStreamer()

// Start configuration server
globalConfigServer = webserver.NewConfigServer("setting")
globalConfigServer.StartInBackground()

return nil
}

Expand All @@ -89,6 +98,9 @@ func setupCleanup() {
if globalWebServer != nil {
globalWebServer.Stop()
}
if globalConfigServer != nil {
globalConfigServer.Stop()
}
if globalFileManager != nil {
globalFileManager.Cleanup()
}
Expand Down Expand Up @@ -216,6 +228,9 @@ func demoCmd(args []string) {
if globalWebServer != nil {
globalWebServer.Stop()
}
if globalConfigServer != nil {
globalConfigServer.Stop()
}
if globalFileManager != nil {
globalFileManager.Cleanup()
}
Expand Down
31 changes: 31 additions & 0 deletions go/settings/env.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package settings

import (
"bufio"
"os"
"strings"
)

// LoadEnv reads key=value pairs from the given file and sets them as environment variables.
func LoadEnv(path string) error {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()

scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" || strings.HasPrefix(line, "#") {
continue
}
parts := strings.SplitN(line, "=", 2)
if len(parts) == 2 {
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
os.Setenv(key, value)
}
}
return scanner.Err()
}
44 changes: 44 additions & 0 deletions go/webserver/config_server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package webserver

import (
"fmt"
"net/http"
"time"
)

// ConfigServer serves the configuration interface on port 2222.
type ConfigServer struct {
Dir string
server *http.Server
}

// NewConfigServer creates a new configuration server.
func NewConfigServer(dir string) *ConfigServer {
return &ConfigServer{Dir: dir}
}

// StartInBackground starts the server asynchronously.
func (cs *ConfigServer) StartInBackground() chan error {
errChan := make(chan error, 1)
go func() {
fs := http.FileServer(http.Dir(cs.Dir))
cs.server = &http.Server{
Addr: ":2222",
Handler: fs,
}
fmt.Println("⚙️ Configuración disponible en http://localhost:2222")
if err := cs.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
errChan <- err
}
}()
time.Sleep(100 * time.Millisecond)
return errChan
}

// Stop stops the configuration server.
func (cs *ConfigServer) Stop() error {
if cs.server != nil {
return cs.server.Close()
}
return nil
}
2 changes: 2 additions & 0 deletions setting/chat.rn
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# chat preferences
welcome_message=true
4 changes: 4 additions & 0 deletions setting/colors.rn
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Default color settings
prompt_color=cyan
user_color=green
bot_color=yellow
2 changes: 2 additions & 0 deletions setting/pront.rn
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# prompt settings
speed=fast
2 changes: 2 additions & 0 deletions setting/runix.local
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Environment variables for Runix
EXAMPLE_VAR=example