feat(tui,server,config): live config editor + artifact viewer
Two operator features over the existing WebSocket protocol, plus a tui-go nav fix. Config editor (g / palette "config"): - core:config gains a thin registry stack: OrchestrationKnobs ([orchestration] block), CorrexConfigWriter (round-trip TOML serializer; parseToml(write(c))==c), ConfigHolder (AtomicReference live config), and EditableConfig (allowlist of editable fields + patch applier; security fields are absent so they can never be patched). - ConfigService validates -> persists (atomic temp+move) -> swaps the holder -> rebuilds config-derived services. Live-apply is scoped to safe seams: stage timeout, compaction threshold (now a supplier), router knobs (routerFacade rebuild), and personalization/project toggles all take effect on the next session/turn; in-flight sessions keep the config they started with. server.host/port are persisted + badged "restart required", not hot-swapped. - Protocol: GetConfig / UpdateConfig / ConfigSnapshot; TUI overlay with type-aware editing (bool toggle, enum cycle, inline int/string edit) and save. Artifact viewer (v / palette "artifacts"): - Server assembles a session's artifacts from the event log and resolves bytes from the CAS store (StreamQueries.listArtifacts) — a replay-neutral snapshot read. TUI overlay lists artifacts and shows scrollable content. tui-go fix: workflow failure no longer clears selectedID (which yanked the user to the list); listNav lands on the first session when nothing is selected instead of wrapping to a random one. Config is not event-sourced (it lives in TOML); editing stays outside the log.
This commit is contained in:
@@ -137,9 +137,6 @@ func (m *Model) applyServer(msg protocol.ServerMessage) {
|
||||
if s := m.session(msg.SessionID); s != nil {
|
||||
s.Active = false
|
||||
}
|
||||
if msg.SessionID == m.selectedID {
|
||||
m.selectedID = ""
|
||||
}
|
||||
case protocol.TypeStageStarted:
|
||||
if s := m.session(msg.SessionID); s != nil {
|
||||
s.CurrentStage = msg.StageID
|
||||
@@ -277,6 +274,28 @@ func (m *Model) applyServer(msg protocol.ServerMessage) {
|
||||
m.gpuTotalMB = msg.GpuMemoryTotalMb
|
||||
m.gpuUtil = msg.GpuUtilizationPct
|
||||
m.ramMB = msg.ProcessRssMb
|
||||
case protocol.TypeArtifactList:
|
||||
m.artifacts = msg.Artifacts
|
||||
m.artifactsFor = msg.SessionID
|
||||
m.artifactsLoading = false
|
||||
m.artifactScroll = 0
|
||||
if m.artifactsIndex >= len(m.artifacts) {
|
||||
m.artifactsIndex = 0
|
||||
}
|
||||
case protocol.TypeConfigSnapshot:
|
||||
m.configFields = msg.ConfigFields
|
||||
m.configRestart = msg.ConfigRestartRequired
|
||||
m.configLoading = false
|
||||
if msg.ConfigError != nil {
|
||||
m.configError = *msg.ConfigError
|
||||
} else {
|
||||
// A clean snapshot means the staged edits were accepted (or this is a fresh fetch).
|
||||
m.configError = ""
|
||||
m.configStaged = map[string]string{}
|
||||
}
|
||||
if m.configIndex >= len(m.configFields) {
|
||||
m.configIndex = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Background-update badge for non-selected sessions.
|
||||
@@ -290,7 +309,8 @@ func sessionIDOf(msg protocol.ServerMessage) string {
|
||||
case protocol.TypeStageManifest, protocol.TypeSnapshotComplete,
|
||||
protocol.TypeProtocolError, protocol.TypeProviderStatus,
|
||||
protocol.TypeWorkflowList, protocol.TypeRouterResponse,
|
||||
protocol.TypeModelChanged, protocol.TypeModelList, protocol.TypeResourceStatus:
|
||||
protocol.TypeModelChanged, protocol.TypeModelList, protocol.TypeResourceStatus,
|
||||
protocol.TypeArtifactList, protocol.TypeConfigSnapshot:
|
||||
return ""
|
||||
default:
|
||||
return msg.SessionID
|
||||
|
||||
Reference in New Issue
Block a user