Files
HxClaw/cmd/hxclaw/internal/helpers.go

156 lines
3.1 KiB
Go
Raw Normal View History

package internal
import (
"bufio"
"errors"
"io"
"os"
"path/filepath"
"github.com/ergochat/readline"
"github.com/sipeed/picoclaw/pkg/config"
"github.com/sipeed/picoclaw/pkg/logger"
)
// 错误定义
var (
ErrInterrupt = errors.New("interrupt")
ErrEOF = errors.New("EOF")
)
// Logo 是 hxclaw 的 Logo
const Logo = "🦐"
// GetPicoclawHome 返回 picoclaw 的家目录
// 优先级: $PICOCLAW_HOME > ~/.picoclaw
func GetPicoclawHome() string {
return config.GetHome()
}
// LoadConfig 加载配置文件
// 复用 picoclaw 的配置加载逻辑
func LoadConfig() (*config.Config, error) {
cfg, err := config.LoadConfig(GetConfigPath())
if err != nil {
return nil, err
}
logger.SetLevelFromString(cfg.Gateway.LogLevel)
return cfg, nil
}
// GetConfigPath 获取配置文件路径
func GetConfigPath() string {
if configPath := os.Getenv(config.EnvConfig); configPath != "" {
return configPath
}
return filepath.Join(GetPicoclawHome(), "config.json")
}
// Readline 实例包装
type Readline struct {
rl *readline.Instance
}
// NewReadline 创建一个新的 Readline 实例
func NewReadline(prompt string) (*Readline, error) {
// 确保历史文件目录存在
historyDir := filepath.Dir(filepath.Join(GetPicoclawHome(), ".hxclaw_history"))
os.MkdirAll(historyDir, 0755)
rl, err := readline.NewEx(&readline.Config{
Prompt: prompt,
HistoryFile: filepath.Join(GetPicoclawHome(), ".hxclaw_history"),
HistoryLimit: 100,
InterruptPrompt: "^C",
EOFPrompt: "exit",
})
if err != nil {
return nil, err
}
return &Readline{rl: rl}, nil
}
// Readline 读取一行输入
func (r *Readline) Readline() (string, error) {
line, err := r.rl.Readline()
if err != nil {
if err == readline.ErrInterrupt {
return "", ErrInterrupt
}
if err == io.EOF {
return "", ErrEOF
}
return "", err
}
return line, nil
}
// Close 关闭 Readline 实例
func (r *Readline) Close() error {
return r.rl.Close()
}
// SimpleReader 简单输入读取器(无历史记录)
type SimpleReader struct {
reader *bufio.Reader
}
// NewSimpleReader 创建一个新的简单读取器
func NewSimpleReader() *SimpleReader {
return &SimpleReader{
reader: bufio.NewReader(os.Stdin),
}
}
// ReadString 读取一行输入
func (r *SimpleReader) ReadString() (string, error) {
line, err := r.reader.ReadString('\n')
if err != nil {
if err == io.EOF {
return "", ErrEOF
}
return "", err
}
// 去掉换行符
if len(line) > 0 && line[len(line)-1] == '\n' {
line = line[:len(line)-1]
}
return line, nil
}
func FindParagraphEnd(text string, startPos int) int {
if startPos >= len(text) {
return 0
}
inCodeBlock := false
inMathBlock := false
for i := startPos; i < len(text); i++ {
if i+3 < len(text) && text[i:i+3] == "```" {
if !inCodeBlock {
inCodeBlock = true
} else {
inCodeBlock = false
}
continue
}
if i+2 < len(text) && (text[i:i+2] == "$$" || text[i:i+2] == "\\[") {
inMathBlock = !inMathBlock
continue
}
if inCodeBlock || inMathBlock {
continue
}
if i+1 < len(text) && text[i] == '\n' && text[i+1] == '\n' {
return i + 2
}
}
return 0
}