zot/packages/provider/sse.go
patriceckhart fa7d8d8be5 refactor: split source into packages/{provider,core,tui,agent}
Single Go module, four top-level packages under packages/. Import
paths become github.com/patriceckhart/zot/packages/<name>; downstream
consumers can depend on individual packages without pulling the rest.

Layout:
  packages/provider/     LLM clients + catalog
  packages/provider/auth/ credential store + OAuth + login server
  packages/core/         agent loop, sessions, cost
  packages/tui/          terminal toolkit + chat view
  packages/agent/        CLI wiring, system prompt
    extensions/ extproto/ modes/ tools/ skills/ swarm/
    sdk/  (was pkg/zotcore, package renamed zotcore -> sdk)
    ext/  (was pkg/zotext, package renamed zotext -> ext)

internal/ and pkg/ removed. The internal/assets logo moved into
packages/provider/auth/assets.

Public Go SDK identifiers renamed:
  pkg/zotcore (package zotcore) -> packages/agent/sdk (package sdk)
  pkg/zotext  (package zotext)  -> packages/agent/ext (package ext)

This breaks Go-based extensions and embedders; the JSON wire protocol
for extensions and RPC is unchanged, so non-Go extensions, already-
built extension binaries, and zot rpc consumers are unaffected.

Docs, examples, and the built-in write-zot-extension skill updated
for the new paths and identifiers. Shadow-bug fixes in code samples
(ext := ext.New -> e := ext.New).
2026-05-27 09:07:15 +02:00

59 lines
1.1 KiB
Go

package provider
import (
"bufio"
"io"
"strings"
)
// sseEvent is one parsed event from a text/event-stream.
type sseEvent struct {
Event string // value of "event:" field (may be empty)
Data string // concatenated "data:" lines
}
// readSSE reads events from r and sends them on out. It closes out when r
// is exhausted or a read error occurs.
func readSSE(r io.Reader, out chan<- sseEvent) {
defer close(out)
sc := bufio.NewScanner(r)
sc.Buffer(make([]byte, 0, 64*1024), 10*1024*1024)
var ev sseEvent
flush := func() {
if ev.Data == "" && ev.Event == "" {
return
}
out <- ev
ev = sseEvent{}
}
for sc.Scan() {
line := sc.Text()
if line == "" {
flush()
continue
}
if strings.HasPrefix(line, ":") {
// comment / keep-alive
continue
}
field, value, ok := strings.Cut(line, ":")
if !ok {
field = line
value = ""
}
// optional single leading space after ':'
value = strings.TrimPrefix(value, " ")
switch field {
case "event":
ev.Event = value
case "data":
if ev.Data != "" {
ev.Data += "\n"
}
ev.Data += value
}
}
flush()
}