Skip to content

feat(tui): Add configurable Launcher and Gateway process management#909

Merged
imguoguo merged 1 commit intosipeed:mainfrom
taorye:main
Feb 28, 2026
Merged

feat(tui): Add configurable Launcher and Gateway process management#909
imguoguo merged 1 commit intosipeed:mainfrom
taorye:main

Conversation

@taorye
Copy link
Collaborator

@taorye taorye commented Feb 28, 2026

  • Added POSIX support for checking and stopping the gateway process in gateway_posix.go.
  • Implemented Windows support for checking and stopping the gateway process in gateway_windows.go.
  • Introduced a menu system in menu.go for navigating model options, including adding and selecting models.
  • Created a model management interface in model.go to handle model configurations, including validation and testing.
  • Defined UI styles in style.go for consistent application theming.
  • Established the main entry point in main.go to run the application.
  • Updated go.mod and go.sum to include necessary dependencies for TUI components.

📝 Description

🗣️ Type of Change

  • 🐞 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • 📖 Documentation update
  • ⚡ Code refactoring (no functional changes, no api changes)

🤖 AI Code Generation

  • 🤖 Fully AI-generated (100% AI, 0% Human)
  • 🛠️ Mostly AI-generated (AI draft, Human verified/modified)
  • 👨‍💻 Mostly Human-written (Human lead, AI assisted or none)

🔗 Related Issue

Closes #805

📚 Technical Context (Skip for Docs)

  • Reference URL:
  • Reasoning:

🧪 Test Environment

  • Hardware: PC
  • OS: Arch
  • Model/Provider: OpenRouter
  • Channels: Telegram

📸 Evidence (Optional)

Click to view Logs/Screenshots

☑️ Checklist

  • My code/docs follow the style of this project.
  • I have performed a self-review of my own changes.
  • I have updated the documentation accordingly.

Copilot AI review requested due to automatic review settings February 28, 2026 11:18
@taorye taorye added the type: enhancement New feature or request label Feb 28, 2026
@imguoguo imguoguo requested review from imguoguo and lxowalle and removed request for Copilot February 28, 2026 11:21
- Implement POSIX-specific gateway process management in gateway_posix.go
- Implement Windows-specific gateway process management in gateway_windows.go
- Create a menu system in menu.go for user interaction
- Develop model management functionality in model.go, including adding, deleting, and testing models
- Introduce a style configuration in style.go for consistent UI appearance
- Set up the main application entry point in main.go
- Update go.mod and go.sum to include necessary dependencies for tcell and tview
Copilot AI review requested due to automatic review settings February 28, 2026 11:35
@imguoguo imguoguo merged commit 27e988c into sipeed:main Feb 28, 2026
4 checks passed
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new picoclaw-launcher-tui terminal UI to configure ~/.picoclaw/config.json and manage starting/stopping picoclaw gateway, aiming to simplify setup and operation from the terminal.

Changes:

  • Introduces a new TUI binary (cmd/picoclaw-launcher-tui) with menus/forms for model + channel configuration.
  • Adds cross-platform gateway process detection/stop helpers (POSIX + Windows) and integrates them into the TUI.
  • Updates release packaging (.goreleaser.yaml) and module deps (go.mod/go.sum) for tview/tcell.

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 16 comments.

Show a summary per file
File Description
cmd/picoclaw-launcher-tui/main.go Entry point for the new TUI binary.
cmd/picoclaw-launcher-tui/internal/ui/app.go Main app state, navigation stack, config save/apply/discard, and gateway/talk launch actions.
cmd/picoclaw-launcher-tui/internal/ui/menu.go Menu table abstraction used throughout the TUI.
cmd/picoclaw-launcher-tui/internal/ui/style.go Global theme + banner view.
cmd/picoclaw-launcher-tui/internal/ui/model.go Model list menu, model edit form, and a “Test” action.
cmd/picoclaw-launcher-tui/internal/ui/channel.go Channel enablement menu + per-channel edit forms.
cmd/picoclaw-launcher-tui/internal/ui/gateway_posix.go POSIX gateway detection/stop helpers.
cmd/picoclaw-launcher-tui/internal/ui/gateway_windows.go Windows gateway detection/stop helpers.
cmd/picoclaw-launcher-tui/internal/config/store.go Config path/load/save helpers for ~/.picoclaw/config.json.
.goreleaser.yaml Adds build + packaging entry for the new TUI binary.
go.mod / go.sum Adds tview/tcell (and transitive deps).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +308 to +317
if !strings.HasPrefix(modelID, "openai/") {
s.showMessage("Unsupported model", "Only openai/* models are supported for test")
return
}
modelName := strings.TrimPrefix(modelID, "openai/")
endpoint := strings.TrimRight(base, "/") + "/chat/completions"

payload := fmt.Sprintf(
`{"model":"%s","messages":[{"role":"user","content":"ping"}],"max_tokens":1}`,
modelName,
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "Test" action currently hard-codes support to openai/* models and also strips the openai/ prefix before sending the request. This is likely incompatible with other supported protocols (e.g., openrouter/*, groq/*, etc.) and even some OpenAI-compatible gateways that expect the full model slug. Consider reusing the existing provider factory (pkg/providers.CreateProviderFromConfig) or at least using the configured protocol/model semantics consistently.

Suggested change
if !strings.HasPrefix(modelID, "openai/") {
s.showMessage("Unsupported model", "Only openai/* models are supported for test")
return
}
modelName := strings.TrimPrefix(modelID, "openai/")
endpoint := strings.TrimRight(base, "/") + "/chat/completions"
payload := fmt.Sprintf(
`{"model":"%s","messages":[{"role":"user","content":"ping"}],"max_tokens":1}`,
modelName,
endpoint := strings.TrimRight(base, "/") + "/chat/completions"
// Use the configured model identifier as-is so that different
// providers/protocols and gateways that rely on full slugs work.
payload := fmt.Sprintf(
`{"model":"%s","messages":[{"role":"user","content":"ping"}],"max_tokens":1}`,
modelID,

Copilot uses AI. Check for mistakes.
_ = logFile.Close()
return
}
_ = logFile.Close()
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After cmd.Start(), the code never calls cmd.Wait(). On Unix this can leave a zombie process when the gateway exits, and it also means s.gatewayCmd won't reflect an unexpectedly terminated gateway. Consider starting a goroutine to Wait() and update state (or use cmd.Run() if you intend to block).

Suggested change
_ = logFile.Close()
_ = logFile.Close()
go func(c *exec.Cmd) {
_ = c.Wait()
}(cmd)

Copilot uses AI. Check for mistakes.
Comment on lines +6 to +10
import "os/exec"

func isGatewayProcessRunning() bool {
cmd := exec.Command("tasklist", "/FI", "IMAGENAME eq picoclaw.exe")
return cmd.Run() == nil
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On Windows, tasklist returns exit code 0 even when no processes match the filter, so cmd.Run() == nil will almost always report the gateway as running. Capture and inspect the output (or use PowerShell/WMI to query processes) to determine whether a matching process actually exists.

Suggested change
import "os/exec"
func isGatewayProcessRunning() bool {
cmd := exec.Command("tasklist", "/FI", "IMAGENAME eq picoclaw.exe")
return cmd.Run() == nil
import (
"bytes"
"os/exec"
)
func isGatewayProcessRunning() bool {
cmd := exec.Command("tasklist", "/FI", "IMAGENAME eq picoclaw.exe")
output, err := cmd.Output()
if err != nil {
return false
}
return bytes.Contains(output, []byte("picoclaw.exe"))

Copilot uses AI. Check for mistakes.
Comment on lines +208 to +212
if valid {
color := tview.Styles.PrimaryTextColor
return &color
}
color := tcell.ColorGray
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

modelStatusColor accepts a selected parameter that is never used. Either remove the parameter to avoid confusion, or use it to visually distinguish the active/default model from other valid models.

Suggested change
if valid {
color := tview.Styles.PrimaryTextColor
return &color
}
color := tcell.ColorGray
var color tcell.Color
switch {
case !valid:
color = tcell.ColorGray
case selected:
color = tview.Styles.ContrastSecondaryTextColor
default:
color = tview.Styles.PrimaryTextColor
}

Copilot uses AI. Check for mistakes.
Comment on lines +9 to +14
cmd := exec.Command("sh", "-c", "pgrep -f 'picoclaw\\s+gateway' >/dev/null 2>&1")
return cmd.Run() == nil
}

func stopGatewayProcess() error {
cmd := exec.Command("sh", "-c", "pkill -f 'picoclaw\\s+gateway' >/dev/null 2>&1")
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The POSIX gateway process match pattern uses \s inside a pgrep -f regex, but pgrep uses POSIX ERE where \s is not a whitespace class. This likely prevents detecting a running gateway. Use a POSIX class like [[:space:]]+ (or a simpler literal picoclaw gateway) instead of \s+ for both pgrep/pkill commands.

Suggested change
cmd := exec.Command("sh", "-c", "pgrep -f 'picoclaw\\s+gateway' >/dev/null 2>&1")
return cmd.Run() == nil
}
func stopGatewayProcess() error {
cmd := exec.Command("sh", "-c", "pkill -f 'picoclaw\\s+gateway' >/dev/null 2>&1")
cmd := exec.Command("sh", "-c", "pgrep -f 'picoclaw[[:space:]]+gateway' >/dev/null 2>&1")
return cmd.Run() == nil
}
func stopGatewayProcess() error {
cmd := exec.Command("sh", "-c", "pkill -f 'picoclaw[[:space:]]+gateway' >/dev/null 2>&1")

Copilot uses AI. Check for mistakes.
Comment on lines +194 to +203
form.AddInputField("Token", cfg.Token, 128, nil, func(text string) {
cfg.Token = strings.TrimSpace(text)
})
form.AddInputField("Proxy", cfg.Proxy, 128, nil, func(text string) {
cfg.Proxy = strings.TrimSpace(text)
})
form.AddInputField("Allow From", strings.Join(cfg.AllowFrom, ","), 128, nil, func(text string) {
cfg.AllowFrom = splitCSV(text)
})
return wrapWithBack(form, s)
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most channel form input handlers (e.g., Token/Proxy/Allow From) update config but do not set s.dirty = true, so users can exit without a save prompt and lose changes. Mark the state dirty in these callbacks (similar to the Enabled checkbox) and refresh the main menu if you want the UI to reflect readiness immediately.

Copilot uses AI. Check for mistakes.
Comment on lines +279 to +288
func (s *appState) viewGatewayLog() {
data, err := os.ReadFile(s.logPath)
if err != nil {
s.showMessage("Log not found", "gateway.log not found")
return
}
text := tview.NewTextView()
text.SetBorder(true).SetTitle("Gateway Log")
text.SetText(string(data))
text.SetDoneFunc(func(key tcell.Key) {
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

viewGatewayLog reads the entire log file into memory with no size limit. Since the log is appended to indefinitely, this can lead to high memory usage or OOM. Consider limiting the read (e.g., last N bytes/lines) or streaming into the TextView.

Copilot uses AI. Check for mistakes.
}
_ = stopGatewayProcess()
cmd := exec.Command("picoclaw", "gateway")
logFile, err := os.OpenFile(s.logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gateway.log is created with mode 0644. Gateway output can include sensitive data (tokens, request payloads, etc.), so this should likely be owner-readable only (0600) to match how config files are written in this repo.

Suggested change
logFile, err := os.OpenFile(s.logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)
logFile, err := os.OpenFile(s.logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o600)

Copilot uses AI. Check for mistakes.
Comment on lines +348 to +353
s.app.Suspend(func() {
cmd := exec.Command("picoclaw", "agent")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
_ = cmd.Run()
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TUI shells out to picoclaw by name only. If picoclaw isn't on PATH (common for bundled releases), Start Talk/Gateway will fail. Consider resolving the executable similarly to cmd/picoclaw-launcher (prefer picoclaw in the same directory as the launcher, then fallback to PATH).

Copilot uses AI. Check for mistakes.
Comment on lines +315 to +318
payload := fmt.Sprintf(
`{"model":"%s","messages":[{"role":"user","content":"ping"}],"max_tokens":1}`,
modelName,
)
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

testModel builds JSON with fmt.Sprintf and string interpolation. If modelName contains quotes or other special characters, this will produce invalid JSON. Prefer marshaling a struct/map with encoding/json to ensure correct escaping.

Copilot uses AI. Check for mistakes.
vvr3ddy added a commit to vvr3ddy/picoclaw that referenced this pull request Mar 1, 2026
Changes from upstream:
- fix(channel): config cleanup and regex precompile (sipeed#911, sipeed#916)
- fix(github_copilot): improve error handling (sipeed#919)
- fix(wecom): context leaks and data race fixes (sipeed#914, sipeed#918)
- fix(tools): HTTP client caching and response body cleanup (sipeed#940)
- feat(tui): Add configurable Launcher and Gateway process management (sipeed#909)
- feat(migrate): Update migration system with openclaw support (sipeed#910)
- fix(skills): Use registry-backed search for skills discovery (sipeed#929)
- build: Add armv6 support to goreleaser (sipeed#905)
- docs: Sync READMEs and channel documentation
@Orgmar
Copy link
Contributor

Orgmar commented Mar 2, 2026

@taorye The TUI launcher with gateway process management is a great addition. Having cross-platform support for both POSIX and Windows with the menu system for model configuration makes the setup experience much smoother.

We're building a PicoClaw Dev Group on Discord for contributors to connect and collaborate. If you'd like to join, send an email to support@sipeed.com with the subject [Join PicoClaw Dev Group] taorye and we'll send you the invite link.

liangzhang-keepmoving pushed a commit to liangzhang-keepmoving/picoclaw that referenced this pull request Mar 2, 2026
…ipeed#909)

- Implement POSIX-specific gateway process management in gateway_posix.go
- Implement Windows-specific gateway process management in gateway_windows.go
- Create a menu system in menu.go for user interaction
- Develop model management functionality in model.go, including adding, deleting, and testing models
- Introduce a style configuration in style.go for consistent UI appearance
- Set up the main application entry point in main.go
- Update go.mod and go.sum to include necessary dependencies for tcell and tview
hyperwd pushed a commit to hyperwd/picoclaw that referenced this pull request Mar 5, 2026
…ipeed#909)

- Implement POSIX-specific gateway process management in gateway_posix.go
- Implement Windows-specific gateway process management in gateway_windows.go
- Create a menu system in menu.go for user interaction
- Develop model management functionality in model.go, including adding, deleting, and testing models
- Introduce a style configuration in style.go for consistent UI appearance
- Set up the main application entry point in main.go
- Update go.mod and go.sum to include necessary dependencies for tcell and tview
Pluckypan pushed a commit to Pluckypan/picoclaw that referenced this pull request Mar 6, 2026
…ipeed#909)

- Implement POSIX-specific gateway process management in gateway_posix.go
- Implement Windows-specific gateway process management in gateway_windows.go
- Create a menu system in menu.go for user interaction
- Develop model management functionality in model.go, including adding, deleting, and testing models
- Introduce a style configuration in style.go for consistent UI appearance
- Set up the main application entry point in main.go
- Update go.mod and go.sum to include necessary dependencies for tcell and tview
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Add TUI support via new repository picoclaw_tui

4 participants