feat: expand companion platform, device controls, and memoir workflows
ci / verify (push) Canceled after 0s

Add customer and operator login, BLE provisioning, device fleet controls, call memory, memoir generation, and refreshed family and maintenance portals. Include API contracts, migrations, firmware sources, tests, assets, and deployment documentation.
This commit is contained in:
Codex Local
2026-10-08 10:06:44 +08:00
parent 98fb2965d0
commit bf1233d5df
861 changed files with 94614 additions and 3209 deletions
+113
View File
@@ -0,0 +1,113 @@
package elderagent
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestProviderCreateAndRecover(t *testing.T) {
var created, updated int
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" || r.URL.Query().Get("Version") != "2025-08-01" || r.Header.Get("Authorization") == "" {
t.Error("incorrect signed API request")
}
var body map[string]any
json.NewDecoder(r.Body).Decode(&body)
switch r.URL.Query().Get("Action") {
case "AibotQuery":
if body["AccessType"] != "public" {
t.Error("name lookup requires public AccessType")
}
if created == 0 {
fmt.Fprint(w, `{"Result":{"Bots":[]}}`)
} else {
fmt.Fprint(w, `{"Result":{"Bots":[{"Id":"bot-elder","Name":"elder-1"}]}}`)
}
case "AibotCreate":
created++
if body["AccessType"] != "public" || body["Config"] == nil || body["Name"] != "elder-1" {
t.Error("missing creation fields")
}
fmt.Fprint(w, `{"Result":{"Id":"bot-elder"}}`)
case "AibotUpdate":
updated++
fmt.Fprint(w, `{"Result":{}}`)
default:
t.Error("unexpected action")
}
}))
defer server.Close()
c := NewClient("test-ak", "test-sk", "", server.URL, "")
for n := 0; n < 2; n++ {
b, err := c.Ensure(context.Background(), "elder-1", map[string]any{"S2SConfig": map[string]any{}}, map[string]any{})
if err != nil || b.ID != "bot-elder" {
t.Fatal(b, err)
}
}
if created != 1 || updated != 1 {
t.Fatal(created, updated)
}
}
func TestConfigurationIsPerElderAndKeepsS2SOnServer(t *testing.T) {
s := Service{Template: []byte(`{"templates":{"s2s_hybrid":{"Config":{"S2SConfig":{"Provider":"volcano","OutputMode":1,"ProviderParams":{"app":{"appid":"test-app","token":"test-token"},"dialog":{"system_role":"温暖陪伴","dialog_id":"shared"}}},"MemoryConfig":{"Enable":true}}}}}`), CallbackURL: "https://example.com/callback", CallbackSecret: []byte(strings.Repeat("k", 32))}
a, aa, err := s.Configuration("elder-1", "张奶奶")
if err != nil {
t.Fatal(err)
}
b, ba, err := s.Configuration("elder-2", "李爷爷")
if err != nil {
t.Fatal(err)
}
rawA, _ := json.Marshal(a)
rawB, _ := json.Marshal(b)
if !strings.Contains(string(rawA), "张奶奶") || strings.Contains(string(rawB), "张奶奶") || strings.Contains(string(rawA), "dialog_id") {
t.Fatal("elder config isolation failed")
}
if a["S2SConfig"].(map[string]any)["OutputMode"] != 0 || aa["ServerMessageURLForRTS"] == ba["ServerMessageURLForRTS"] {
t.Fatal("incorrect S2S/callback config")
}
if a["MemoryConfig"].(map[string]any)["Enable"] != false {
t.Fatal("inherited shared memory")
}
}
func TestProviderErrorDoesNotExposeConfiguration(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, `{"ResponseMetadata":{"Error":{"Code":"InvalidParameter","Message":"secret-token"}}}`)
}))
defer server.Close()
_, err := NewClient("ak", "sk", "", server.URL, "").Ensure(context.Background(), "elder", nil, nil)
if err == nil || strings.Contains(err.Error(), "secret-token") {
t.Fatal(err)
}
}
func TestPersonaReadbackChecksManagedFieldsAndMasksErrors(t *testing.T) {
config := map[string]any{"S2SConfig": map[string]any{"ProviderParams": map[string]any{"app": map[string]any{"token": "SECRET_TOKEN"}, "dialog": map[string]any{"system_role": "新角色"}}}, "MemoryConfig": map[string]any{"Enable": false}}
agent := map[string]any{"WelcomeMessage": "开场", "ServerMessageURLForRTS": "https://example.com/callback", "ServerMessageSignatureForRTS": "signature"}
stale := false
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Query().Get("Action") != "AibotQuery" {
t.Error("wrong verification action")
}
role := "新角色"
if stale {
role = "SECRET_STALE_ROLE"
}
actual := map[string]any{"S2SConfig": map[string]any{"ProviderParams": map[string]any{"dialog": map[string]any{"system_role": role}}}, "MemoryConfig": map[string]any{"Enable": false}}
json.NewEncoder(w).Encode(map[string]any{"Result": map[string]any{"Bots": []Bot{{ID: "bot", Config: actual, AgentConfig: agent}}}})
}))
defer server.Close()
c := NewClient("ak", "sk", "", server.URL, "")
if err := c.VerifyConfiguration(context.Background(), "bot", config, agent); err != nil {
t.Fatal("masked credentials failed verification", err)
}
stale = true
if err := c.VerifyConfiguration(context.Background(), "bot", config, agent); err == nil || strings.Contains(err.Error(), "SECRET") {
t.Fatal("unverified or exposed provider config", err)
}
}