diff --git a/cmd/homed/main.go b/cmd/homed/main.go index c4a8335..096f2c5 100644 --- a/cmd/homed/main.go +++ b/cmd/homed/main.go @@ -303,7 +303,7 @@ func main() { // === Built-in HTTP API & WebUI Plugin === webui := api.NewWebUIPlugin( "webui", cfg.Daemon.ListenAddr, - sup, memDB, skMgr, luaVM, cfg, iom, textMem, ks, trk, + sup, memDB, skMgr, luaVM, cfg, iom, textMem, ks, trk, cfgReg, pluginReg, ) iom.RegisterDevice(webui) webui.Start() diff --git a/internal/api/handler.go b/internal/api/handler.go index 4af44ad..ffa4dd6 100644 --- a/internal/api/handler.go +++ b/internal/api/handler.go @@ -5,15 +5,18 @@ import ( "fmt" "net/http" "os" + "sort" "strconv" "strings" "time" agentIO "gitcode.com/JianFeeeee/HomeAgent/internal/agent/io" + internalConfig "gitcode.com/JianFeeeee/HomeAgent/internal/config" "gitcode.com/JianFeeeee/HomeAgent/internal/knowledge" luaVM "gitcode.com/JianFeeeee/HomeAgent/internal/lua" "gitcode.com/JianFeeeee/HomeAgent/internal/memory" "gitcode.com/JianFeeeee/HomeAgent/internal/memory/text" + "gitcode.com/JianFeeeee/HomeAgent/internal/plugin" "gitcode.com/JianFeeeee/HomeAgent/internal/skill" "gitcode.com/JianFeeeee/HomeAgent/internal/supervisor" "gitcode.com/JianFeeeee/HomeAgent/internal/tracker" @@ -32,9 +35,11 @@ type Handler struct { textMem *text.Memory knowledge *knowledge.Store tracker *tracker.Tracker + cfgReg *internalConfig.ConfigRegistry + pluginReg *plugin.Registry } -func NewHandler(sup *supervisor.Daemon, mem *memory.GraphDB, sk *skill.Manager, lua *luaVM.VM, cfg *types.Config, iom *agentIO.IOManager, tm *text.Memory, ks *knowledge.Store, tr *tracker.Tracker) *Handler { +func NewHandler(sup *supervisor.Daemon, mem *memory.GraphDB, sk *skill.Manager, lua *luaVM.VM, cfg *types.Config, iom *agentIO.IOManager, tm *text.Memory, ks *knowledge.Store, tr *tracker.Tracker, cr *internalConfig.ConfigRegistry, pr *plugin.Registry) *Handler { var idx *memory.Indexer if mem != nil { idx = memory.NewIndexer(mem) @@ -51,6 +56,8 @@ func NewHandler(sup *supervisor.Daemon, mem *memory.GraphDB, sk *skill.Manager, textMem: tm, knowledge: ks, tracker: tr, + cfgReg: cr, + pluginReg: pr, } } @@ -66,6 +73,8 @@ func (h *Handler) RegisterRoutes(mux *http.ServeMux) { mux.HandleFunc("/api/v1/memory/text", h.handleTextMemory) mux.HandleFunc("/api/v1/network", h.handleNetwork) mux.HandleFunc("/api/v1/config", h.handleConfig) + mux.HandleFunc("/api/v1/settings", h.handleSettings) + mux.HandleFunc("/api/v1/settings/", h.handleSettings) mux.HandleFunc("/api/v1/knowledge", h.handleKnowledge) mux.HandleFunc("/api/v1/knowledge/", h.handleKnowledge) mux.HandleFunc("/api/v1/adapters", h.handleAdapters) @@ -506,6 +515,51 @@ func (h *Handler) handleConfig(w http.ResponseWriter, r *http.Request) { } } +func (h *Handler) handleSettings(w http.ResponseWriter, r *http.Request) { + if h.cfgReg == nil { + writeJSON(w, http.StatusNotFound, map[string]string{"error": "config registry not available"}) + return + } + switch r.Method { + case http.MethodGet: + prefix := r.URL.Query().Get("prefix") + keys := h.cfgReg.List(prefix) + values := make(map[string]interface{}) + for _, k := range keys { + v, _ := h.cfgReg.Get(k) + values[k] = v + } + // 返回插件列表供侧边栏分组 + plugins := []string{"core"} + if h.pluginReg != nil { + for _, p := range h.pluginReg.List() { + plugins = append(plugins, "plugin."+p.Name()) + } + } + sort.Strings(plugins) + writeJSON(w, http.StatusOK, map[string]interface{}{ + "settings": values, + "plugins": plugins, + }) + case http.MethodPut: + var body struct { + Key string `json:"key"` + Value interface{} `json:"value"` + } + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid request"}) + return + } + if err := h.cfgReg.Set(body.Key, body.Value); err != nil { + writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) + return + } + writeJSON(w, http.StatusOK, map[string]string{"status": "ok"}) + default: + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) + } +} + // OpenAI 兼容 API — 所有输入走 IO 抽象层(中断) func (h *Handler) handleOpenAICompletions(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { @@ -664,6 +718,15 @@ input,textarea,select{background:#0f172a;border:1px solid #334155;border-radius: label{display:block;font-size:12px;color:#94a3b8;margin-bottom:4px} h3{font-size:14px;font-weight:600;color:#f1f5f9;margin-bottom:8px} pre{background:#0f172a;border-radius:6px;padding:12px;font-size:12px;overflow-x:auto;color:#a5b4fc} +.settings-layout{display:flex;gap:20px;min-height:60vh} +.settings-sidebar{width:200px;flex-shrink:0;background:#1e293b;border:1px solid #334155;border-radius:12px;padding:12px 0;overflow-y:auto} +.settings-sidebar a{display:block;padding:10px 16px;color:#94a3b8;font-size:13px;cursor:pointer;text-decoration:none;border-left:3px solid transparent} +.settings-sidebar a:hover{background:#0f172a;color:#e2e8f0} +.settings-sidebar a.active{background:#0f172a;color:#38bdf8;border-left-color:#38bdf8} +.settings-content{flex:1;min-width:0} +.settings-key{font-family:monospace;font-size:12px;color:#64748b;margin-bottom:2px} +.save-btn{float:right} +.toast{position:fixed;bottom:20px;right:20px;background:#166534;color:#86efac;padding:10px 20px;border-radius:8px;font-size:13px;display:none;z-index:100} @@ -682,17 +745,23 @@ pre{background:#0f172a;border-radius:6px;padding:12px;font-size:12px;overflow-x:
+
diff --git a/internal/api/handler_test.go b/internal/api/handler_test.go index 499a185..de08f06 100644 --- a/internal/api/handler_test.go +++ b/internal/api/handler_test.go @@ -25,7 +25,7 @@ func newTestHandler(t *testing.T) (*Handler, *supervisor.Daemon) { sup := supervisor.New(cfg) sup.Start() - return NewHandler(sup, nil, nil, nil, cfg, nil, nil, nil, nil), sup + return NewHandler(sup, nil, nil, nil, cfg, nil, nil, nil, nil, nil, nil), sup } func TestHandleStatus(t *testing.T) { @@ -129,7 +129,7 @@ func TestHandleKnowledgeSearch(t *testing.T) { sup.Start() defer sup.Shutdown() - h := NewHandler(sup, nil, nil, nil, cfg, nil, nil, ks, nil) + h := NewHandler(sup, nil, nil, nil, cfg, nil, nil, ks, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/api/v1/knowledge?q=test", nil) w := httptest.NewRecorder() @@ -161,7 +161,7 @@ func TestHandleKnowledgeCreate(t *testing.T) { sup.Start() defer sup.Shutdown() - h := NewHandler(sup, nil, nil, nil, cfg, nil, nil, ks, nil) + h := NewHandler(sup, nil, nil, nil, cfg, nil, nil, ks, nil, nil, nil) body := `{"name":"new_doc","content":"fresh content"}` req := httptest.NewRequest(http.MethodPost, "/api/v1/knowledge", strings.NewReader(body)) @@ -226,7 +226,7 @@ func TestHandleTrackerStats(t *testing.T) { sup.Start() defer sup.Shutdown() - h := NewHandler(sup, nil, nil, nil, cfg, nil, nil, nil, tr) + h := NewHandler(sup, nil, nil, nil, cfg, nil, nil, nil, tr, nil, nil) req := httptest.NewRequest(http.MethodGet, "/api/v1/tracker", nil) w := httptest.NewRecorder() diff --git a/internal/api/plugin.go b/internal/api/plugin.go index 6e7c3a9..e81e664 100644 --- a/internal/api/plugin.go +++ b/internal/api/plugin.go @@ -5,10 +5,12 @@ import ( "net/http" agentIO "gitcode.com/JianFeeeee/HomeAgent/internal/agent/io" + internalConfig "gitcode.com/JianFeeeee/HomeAgent/internal/config" "gitcode.com/JianFeeeee/HomeAgent/internal/knowledge" luaVM "gitcode.com/JianFeeeee/HomeAgent/internal/lua" "gitcode.com/JianFeeeee/HomeAgent/internal/memory" "gitcode.com/JianFeeeee/HomeAgent/internal/memory/text" + "gitcode.com/JianFeeeee/HomeAgent/internal/plugin" "gitcode.com/JianFeeeee/HomeAgent/internal/skill" "gitcode.com/JianFeeeee/HomeAgent/internal/supervisor" "gitcode.com/JianFeeeee/HomeAgent/internal/tracker" @@ -26,9 +28,10 @@ type Plugin struct { } func NewWebUIPlugin(name, addr string, sup *supervisor.Daemon, mem *memory.GraphDB, sk *skill.Manager, - lua *luaVM.VM, cfg *types.Config, iom *agentIO.IOManager, tm *text.Memory, ks *knowledge.Store, tr *tracker.Tracker) *Plugin { + lua *luaVM.VM, cfg *types.Config, iom *agentIO.IOManager, tm *text.Memory, ks *knowledge.Store, tr *tracker.Tracker, + cr *internalConfig.ConfigRegistry, pr *plugin.Registry) *Plugin { - h := NewHandler(sup, mem, sk, lua, cfg, iom, tm, ks, tr) + h := NewHandler(sup, mem, sk, lua, cfg, iom, tm, ks, tr, cr, pr) mux := http.NewServeMux() h.RegisterRoutes(mux)