Something went wrong. Try again.
small gleam coding and persistent agent daemon with a detachable cli and a canvas ui forked from okami.mom/albedo
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324//go:build unix
// These scenarios drive the detached CLI and a real daemon: a unit handler test// cannot catch leaked daemon keys, wrong child argv, or stale restart discovery.package e2e
import ( "context" "encoding/json" "io" "net" "net/http" "os" "path/filepath" "runtime" "slices" "strconv" "strings" "testing" "time"
"albedo/cli/internal/daemon" "albedo/cli/internal/webui")
func proxyExtension(t *testing.T, session string, enabled bool) { t.Helper() settings, err := daemon.GetSettings(context.Background(), conn(t)) if err != nil { t.Fatal(err) } if _, err := daemon.SelectExtension(context.Background(), conn(t), session, daemon.ExtensionSelectionRequest{Name: "proxy", Scope: "global", ETag: settings.ETags["extensions"], Enabled: &enabled}); err != nil { t.Fatal(err) }}
func proxyCall(t *testing.T, base, method, path, key, body string) (int, string) { t.Helper() request, err := http.NewRequestWithContext(t.Context(), method, base+path, strings.NewReader(body)) if err != nil { t.Fatal(err) } if key != "" { request.Header.Set("Authorization", "Bearer "+key) } if body != "" { request.Header.Set("Content-Type", "application/json") } client := &http.Client{Timeout: 15 * time.Second} response, err := client.Do(request) if err != nil { t.Fatal(err) } defer response.Body.Close() raw, err := io.ReadAll(response.Body) if err != nil { t.Fatal(err) } return response.StatusCode, string(raw)}
func proxyRecord(t *testing.T) webui.Manifest { t.Helper() raw, err := os.ReadFile(filepath.Join(suite.home, "proxy.json")) if err != nil { t.Fatal(err) } var record webui.Manifest if err := json.Unmarshal(raw, &record); err != nil { t.Fatal(err) } return record}
func TestProxyBackgroundScopesKeyAndRecoversAfterDaemonRestart(t *testing.T) { profile := providerRoute(t, echoReply) session := newSession(t, t.TempDir()) proxyExtension(t, session, false) off := cli(t, "proxy") if off != "the proxy extension is off. turn it on in /extensions, then run albedo proxy again.\n" { t.Fatalf("off: %q", off) } if _, err := os.Stat(filepath.Join(suite.home, "proxy-key")); !os.IsNotExist(err) { t.Fatalf("disabled proxy created key: %v", err) } proxyExtension(t, session, true) t.Cleanup(func() { cli(t, "proxy", "--stop") proxyExtension(t, session, false) }) started := cli(t, "proxy", "--port", "0") record := proxyRecord(t) keyPath := filepath.Join(suite.home, "proxy-key") rawKey, err := os.ReadFile(keyPath) if err != nil { t.Fatal(err) } key := string(rawKey) if info, err := os.Stat(keyPath); err != nil || info.Mode().Perm() != 0o600 { t.Fatalf("key permissions: %v %v", info, err) } for _, text := range []string{"running in the background", "stop it with: albedo proxy --stop", record.URL + "/v1", key, "openai-compatible", "/fixture-model"} { if !strings.Contains(started, text) { t.Fatalf("summary missing %q: %s", text, started) } } if strings.Contains(started, conn(t).Snapshot().Token) { t.Fatal("summary leaked daemon token") } if _, _, err := runCLI("proxy", "--port", "1"); err == nil { t.Fatal("running proxy accepted changed listen options") } repeated := cli(t, "proxy") if !strings.Contains(repeated, "was already running since") || proxyRecord(t).PID != record.PID { t.Fatalf("repeat replaced server: %s", repeated) } for _, candidate := range []string{"", "wrong", conn(t).Snapshot().Token} { if code, _ := proxyCall(t, record.URL, "GET", "/v1/models", candidate, ""); code != 401 { t.Errorf("key %q accepted: %d", candidate, code) } } if code, body := proxyCall(t, record.URL, "GET", "/v1/models", key, ""); code != 200 || !strings.Contains(body, profile+"/fixture-model") { t.Fatalf("models: %d %s", code, body) } for _, path := range []string{"/server", "/api/settings", "/extensions/proxy/v1/models", "/v1/models/../settings", "/v1/%6dodels"} { if code, _ := proxyCall(t, record.URL, "GET", path, key, ""); code != 404 { t.Errorf("non-proxy route %s: %d", path, code) } } if code, _ := proxyCall(t, record.URL, "POST", "/v1/models", key, "{}"); code != 405 { t.Fatalf("models accepted wrong method: %d", code) } body := `{"model":"` + profile + `/fixture-model","messages":[{"role":"user","content":"proxy test"}]}` if code, reply := proxyCall(t, record.URL, "POST", "/v1/chat/completions", key, body); code != 200 || !strings.Contains(reply, "echo: proxy test") { t.Fatalf("completion: %d %s", code, reply) } streaming := strings.Replace(body, `"messages":`, `"stream":true,"messages":`, 1) if code, reply := proxyCall(t, record.URL, "POST", "/v1/chat/completions", key, streaming); code != 200 || !strings.Contains(reply, "data: [DONE]") { t.Fatalf("stream: %d %s", code, reply) } // Rotation revokes the old key without replacing the listener. rotated := cli(t, "proxy", "--new-key") newRaw, err := os.ReadFile(keyPath) if err != nil { t.Fatal(err) } newKey := string(newRaw) if newKey == key || !strings.Contains(rotated, newKey) || proxyRecord(t).PID != record.PID { t.Fatal("rotation did not keep listener and replace key") } if code, _ := proxyCall(t, record.URL, "GET", "/v1/models", key, ""); code != 401 { t.Fatalf("old key accepted: %d", code) } if code, _ := proxyCall(t, record.URL, "GET", "/v1/models", newKey, ""); code != 200 { t.Fatalf("new key refused: %d", code) } // The forwarder stays up while the daemon is down and never restarts it. oldPID := suite.daemonPID if err := daemon.StopDaemon(context.Background(), conn(t), false); err != nil { t.Fatal(err) } if !awaitExit(oldPID, 15*time.Second) { t.Fatal("daemon did not stop") } if code, reply := proxyCall(t, record.URL, "GET", "/v1/models", newKey, ""); code != 503 || !strings.Contains(reply, "the albedo daemon isn't running on this machine") { t.Fatalf("daemon down: %d %s", code, reply) } cli(t, "sessions") snapshot, err := awaitDaemon(suite.home) if err != nil { t.Fatal(err) } suite.daemonPID = snapshot.Pid if err := conn(t).Refresh(t.Context()); err != nil { t.Fatal(err) } // One request may discover the new endpoint; it is not replayed automatically. proxyCall(t, record.URL, "GET", "/v1/models", newKey, "") if code, reply := proxyCall(t, record.URL, "POST", "/v1/chat/completions", newKey, body); code != 200 || !strings.Contains(reply, "echo: proxy test") { t.Fatalf("restart: %d %s", code, reply) } log, err := os.ReadFile(filepath.Join(suite.home, "proxy.log")) if err != nil || !strings.Contains(string(log), profile+"/fixture-model") || strings.Contains(string(log), key) || strings.Contains(string(log), snapshot.Token) { t.Fatalf("request log missing or leaked key: %v", err) } stopped := cli(t, "proxy", "--stop") if !strings.Contains(stopped, "stopped albedo proxy on "+record.URL+"/v1") { t.Fatalf("stop: %s", stopped) } if got := cli(t, "proxy", "--stop"); got != "albedo proxy is not running.\n" { t.Fatalf("second stop: %s", got) } // A normal restart preserves the client key. cli(t, "proxy", "--port", "0", "--quiet") persisted, err := os.ReadFile(keyPath) if err != nil || string(persisted) != newKey { t.Fatal("restart changed key") } quietRecord := proxyRecord(t) proxyCall(t, quietRecord.URL, "GET", "/v1/models", newKey, "") if log, err := os.ReadFile(filepath.Join(suite.home, "proxy.log")); err != nil || len(log) != 0 { t.Fatalf("quiet request logged: %q %v", log, err) }}
func TestProxySaveUsesOwnNetworksAndForgetsThem(t *testing.T) { loginManager(t)
providerRoute(t, echoReply) addresses, err := webui.NetworkAddresses() if err != nil { t.Fatal(err) } if len(addresses) == 0 { t.Skip("no non-loopback IPv4 address") } networks := []string{addresses[0] + "/32", "127.0.0.0/8"} session := newSession(t, t.TempDir()) proxyExtension(t, session, true) settings, err := daemon.GetSettings(t.Context(), conn(t)) if err != nil { t.Fatal(err) } webNetworks := []string{"192.0.2.0/24"} if err := daemon.SaveWebAllow(t.Context(), conn(t), settings.UI.ETag, webNetworks); err != nil { t.Fatal(err) } t.Cleanup(func() { cli(t, "proxy", "--save") ctx := context.Background() current, err := daemon.GetSettings(ctx, conn(t)) if err != nil { t.Fatal(err) } if err := daemon.SaveProxyAllow(ctx, conn(t), current.UI.ETag, settings.UI.ProxyAllow); err != nil { t.Fatal(err) } current, err = daemon.GetSettings(ctx, conn(t)) if err != nil { t.Fatal(err) } if err := daemon.SaveWebAllow(ctx, conn(t), current.UI.ETag, settings.UI.WebAllow); err != nil { t.Fatal(err) } proxyExtension(t, session, false) }) checkSaved := func(want []string) { t.Helper() current, err := daemon.GetSettings(t.Context(), conn(t)) if err != nil { t.Fatal(err) } if !slices.Equal(current.UI.ProxyAllow, want) || !slices.Equal(current.UI.WebAllow, webNetworks) { t.Fatalf("saved proxy %v, web %v; want proxy %v, web %v", current.UI.ProxyAllow, current.UI.WebAllow, want, webNetworks) } } cli(t, "proxy", "--port", "0", "--allow", networks[0], "--allow", networks[1], "--save") checkSaved(networks) first := proxyRecord(t) failure := filepath.Join(suite.home, "service-stub-fail") if err := os.WriteFile(failure, nil, 0o600); err != nil { t.Fatal(err) } if _, _, err := runCLI("proxy", "--stop"); err == nil { t.Fatal("manager failure reported a successful stop") } if err := os.Remove(failure); err != nil { t.Fatal(err) } if !webui.ManifestAlive(first) { t.Fatal("manager failure killed listener outside the manager") } cli(t, "proxy") if second := proxyRecord(t); second.PID != first.PID { t.Fatal("bare start duplicated the managed listener") } // Updating the service works; invalid input must not change its saved networks. cli(t, "proxy", "--port", "0", "--allow", networks[0], "--allow", networks[1], "--save") if _, _, err := runCLI("proxy", "--allow", "invalid", "--save"); err == nil { t.Fatal("accepted invalid save") } checkSaved(networks) cli(t, "proxy", "--stop") cli(t, "proxy") record := proxyRecord(t) if !slices.Equal(record.Allow, networks) || !slices.Contains(record.Addresses, addresses[0]) { t.Fatalf("bare start ignored saved networks: %+v", record) } key, err := os.ReadFile(filepath.Join(suite.home, "proxy-key")) if err != nil { t.Fatal(err) } shared := "http://" + net.JoinHostPort(addresses[0], strconv.Itoa(record.Port)) if code, body := proxyCall(t, shared, "GET", "/v1/models", string(key), ""); code != http.StatusOK { t.Fatalf("shared listener: %d %s", code, body) } cli(t, "proxy", "--stop") cli(t, "proxy", "--save") checkSaved(nil) if _, err := os.Stat(filepath.Join(suite.home, "proxy.json")); !os.IsNotExist(err) { t.Fatalf("forget left listener: %v", err) } raw, err := os.ReadFile(filepath.Join(suite.home, "service-stub.log")) if err != nil { t.Fatal(err) } for _, operation := range []string{"start", "stop"} { if runtime.GOOS == "darwin" { operation = map[string]string{"start": "bootstrap", "stop": "bootout"}[operation] } if !strings.Contains(string(raw), operation) { t.Fatalf("manager never received %s: %s", operation, raw) } } state, err := os.ReadFile(filepath.Join(suite.home, "proxy-service-stub.json")) if err == nil { t.Fatalf("forget left managed process: %s", state) } cli(t, "proxy") if record := proxyRecord(t); len(record.Allow) != 0 || len(record.Addresses) != 0 { t.Fatalf("bare start restored forgotten networks: %+v", record) }}