mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-22 18:08:04 +00:00
- 所有内置插件 init() 自注册 (plugin.RegisterFactory), 移除 main.go 硬编码 - 新增 .so 动态加载器 (internal/plugin/dynamic.go), 插件可编译为 plugin.so - 新增 plugin.json 元数据 (internal/plugin/manifest.go) - 新增 interceptLoop 独立 goroutine: (a) cancelLLM() 取消进行中的 HTTP 请求 (b) interceptCh → drainInterrupt() 注入 [打断消息] 到 LLM 上下文 (c) InjectInput 空闲时触发新处理循环 - 新增 internal/plugins/all.go 空白导入触发所有内置插件 init() - internal/sdk/ 作为 PluginSDK 正式 Go API - internal/api/ → internal/plugins/webui/ 迁移 - 删除旧 cmd/cli/, 使用 cmd/waiter/ 替代 - 更新 PLAN.md / ARCHITECTURE.md / README.md 文档
181 lines
3.9 KiB
Go
181 lines
3.9 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
func main() {
|
|
socket := flag.String("socket", "/var/lib/homeagent/cli.sock", "unix socket path")
|
|
remote := flag.String("remote", "", "remote webui URL (e.g. http://127.0.0.1:8080)")
|
|
say := flag.String("say", "", "send a message and print response (one-shot, no TUI)")
|
|
flag.Parse()
|
|
|
|
if *say != "" {
|
|
if *remote != "" {
|
|
sayRemote(*remote, *say)
|
|
} else {
|
|
sayLocal(*socket, *say)
|
|
}
|
|
return
|
|
}
|
|
|
|
if *remote != "" {
|
|
runRemote(*remote)
|
|
} else {
|
|
runLocal(*socket)
|
|
}
|
|
}
|
|
|
|
// sayLocal sends one message via Unix socket and prints the response
|
|
func sayLocal(socketPath, message string) {
|
|
conn, err := net.Dial("unix", socketPath)
|
|
if err != nil {
|
|
log.Fatalf("connect to %s: %v", socketPath, err)
|
|
}
|
|
defer conn.Close()
|
|
|
|
fmt.Fprintf(conn, "%s\n", message)
|
|
|
|
scanner := bufio.NewScanner(conn)
|
|
scanner.Scan()
|
|
if err := scanner.Err(); err != nil {
|
|
log.Fatalf("read: %v", err)
|
|
}
|
|
|
|
var resp struct {
|
|
Type string `json:"type"`
|
|
Content string `json:"content"`
|
|
Error string `json:"error"`
|
|
}
|
|
if err := json.Unmarshal(scanner.Bytes(), &resp); err != nil {
|
|
fmt.Println(scanner.Text())
|
|
return
|
|
}
|
|
switch resp.Type {
|
|
case "response":
|
|
fmt.Println(resp.Content)
|
|
case "error":
|
|
log.Fatalf("error: %s", resp.Error)
|
|
default:
|
|
fmt.Println(scanner.Text())
|
|
}
|
|
}
|
|
|
|
// sayRemote sends one message via HTTP and prints the response
|
|
func sayRemote(baseURL, message string) {
|
|
baseURL = strings.TrimRight(baseURL, "/")
|
|
body := fmt.Sprintf(`{"message":%q}`, message)
|
|
resp, err := http.Post(baseURL+"/api/v1/chat", "application/json", strings.NewReader(body))
|
|
if err != nil {
|
|
log.Fatalf("http post: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
var result map[string]interface{}
|
|
json.NewDecoder(resp.Body).Decode(&result)
|
|
if content, ok := result["response"].(string); ok {
|
|
fmt.Println(content)
|
|
}
|
|
}
|
|
|
|
// runLocal starts an interactive TUI via Unix socket
|
|
func runLocal(socketPath string) {
|
|
conn, err := net.Dial("unix", socketPath)
|
|
if err != nil {
|
|
log.Fatalf("connect to %s: %v", socketPath, err)
|
|
}
|
|
defer conn.Close()
|
|
|
|
var wg sync.WaitGroup
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
scanner := bufio.NewScanner(conn)
|
|
for scanner.Scan() {
|
|
var resp struct {
|
|
Type string `json:"type"`
|
|
Content string `json:"content"`
|
|
Error string `json:"error"`
|
|
}
|
|
if err := json.Unmarshal(scanner.Bytes(), &resp); err != nil {
|
|
fmt.Println(scanner.Text())
|
|
continue
|
|
}
|
|
switch resp.Type {
|
|
case "response":
|
|
fmt.Println(resp.Content)
|
|
case "error":
|
|
fmt.Fprintf(os.Stderr, "error: %s\n", resp.Error)
|
|
default:
|
|
fmt.Println(scanner.Text())
|
|
}
|
|
}
|
|
}()
|
|
|
|
scanner := bufio.NewScanner(os.Stdin)
|
|
fmt.Print("> ")
|
|
for scanner.Scan() {
|
|
line := strings.TrimSpace(scanner.Text())
|
|
if line == "" {
|
|
fmt.Print("> ")
|
|
continue
|
|
}
|
|
if line == "/exit" || line == "/quit" {
|
|
break
|
|
}
|
|
fmt.Fprintf(conn, "%s\n", line)
|
|
}
|
|
|
|
done := make(chan struct{})
|
|
go func() {
|
|
wg.Wait()
|
|
close(done)
|
|
}()
|
|
select {
|
|
case <-done:
|
|
case <-time.After(60 * time.Second):
|
|
}
|
|
}
|
|
|
|
// runRemote starts an interactive TUI via HTTP
|
|
func runRemote(baseURL string) {
|
|
baseURL = strings.TrimRight(baseURL, "/")
|
|
scanner := bufio.NewScanner(os.Stdin)
|
|
fmt.Print("> ")
|
|
for scanner.Scan() {
|
|
line := strings.TrimSpace(scanner.Text())
|
|
if line == "" {
|
|
fmt.Print("> ")
|
|
continue
|
|
}
|
|
if line == "/exit" || line == "/quit" {
|
|
break
|
|
}
|
|
body := fmt.Sprintf(`{"message":%q}`, line)
|
|
resp, err := http.Post(baseURL+"/api/v1/chat", "application/json", strings.NewReader(body))
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
|
fmt.Print("> ")
|
|
continue
|
|
}
|
|
var result map[string]interface{}
|
|
if resp.StatusCode == http.StatusOK {
|
|
json.NewDecoder(resp.Body).Decode(&result)
|
|
}
|
|
resp.Body.Close()
|
|
if content, ok := result["response"].(string); ok {
|
|
fmt.Println(content)
|
|
}
|
|
fmt.Print("> ")
|
|
}
|
|
}
|