Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ func loadFromEnv() *Config {
}

// SaveConfig stores host and token in both the system keyring and file storage.
// It returns an error only if both storage methods fail.
func SaveConfig(host, token string) error {
if host != "" {
validHost, err := ValidateAndTransformHost(host)
Expand Down Expand Up @@ -163,11 +162,20 @@ func SaveConfig(host, token string) error {
cfg.GleanToken = token
}

if err := saveToFile(cfg); err != nil && keyringErr != nil {
return fmt.Errorf("failed to save config: keyring error: %v, file error: %v", keyringErr, err)
}
fileErr := saveToFile(cfg)

return nil
switch {
case keyringErr != nil && fileErr != nil:
return fmt.Errorf("failed to save config: keyring: %v, file: %v", keyringErr, fileErr)
case fileErr != nil:
cfgLog.Log("warning: config file write failed (keyring OK): %v", fileErr)
return nil
case keyringErr != nil:
cfgLog.Log("keyring unavailable, config saved to file only: %v", keyringErr)
return nil
default:
return nil
}
}

// SaveHostToFile persists only the host in ~/.glean/config.json without touching
Expand Down
4 changes: 2 additions & 2 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,8 @@ func TestConfigOperations(t *testing.T) {
err = SaveConfig("linkedin", "test-token")
assert.Error(t, err)
assert.Contains(t, err.Error(), "failed to save config")
assert.Contains(t, err.Error(), "keyring error")
assert.Contains(t, err.Error(), "file error")
assert.Contains(t, err.Error(), "keyring:")
assert.Contains(t, err.Error(), "file:")

// Reset mock error for other tests
mock.err = nil
Expand Down
4 changes: 3 additions & 1 deletion internal/tui/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,9 @@ func (m *Model) handleSlashCommand(input string) (tea.Model, tea.Cmd) {
// System turns are rendered in the viewport but never sent to the Glean API.
func (m *Model) addSystemMessage(text string) {
turn := Turn{Role: roleSystem, Content: text}
m.session.AppendTurn(turn)
if err := m.session.AppendTurn(turn); err != nil {
sessionLog.Log("save failed: %v", err)
}
if !m.conversationActive {
m.conversationActive = true
m.viewport.Height = m.maxViewportHeight()
Expand Down
8 changes: 6 additions & 2 deletions internal/tui/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,9 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}

// Display turn uses original question for clean viewport rendering.
m.session.AddTurn(roleUser, question, nil)
if err := m.session.AddTurn(roleUser, question, nil); err != nil {
sessionLog.Log("save failed: %v", err)
}

// API message carries file context when present.
apiText := apiContent
Expand Down Expand Up @@ -489,7 +491,9 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
Elapsed: msg.elapsed,
}
m.addTurnToConversation(turn)
m.session.AppendTurn(turn) // preserves Elapsed for renderConversation
if err := m.session.AppendTurn(turn); err != nil {
sessionLog.Log("save failed: %v", err)
}
}
m.viewport.SetContent(m.renderConversation())
m.viewport.GotoBottom()
Expand Down
8 changes: 5 additions & 3 deletions internal/tui/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,17 @@ func (s *Session) Save() error {
}

// AddTurn appends a turn to the session and saves immediately.
func (s *Session) AddTurn(role, content string, sources []Source) {
s.AppendTurn(Turn{Role: role, Content: content, Sources: sources})
func (s *Session) AddTurn(role, content string, sources []Source) error {
return s.AppendTurn(Turn{Role: role, Content: content, Sources: sources})
}

// AppendTurn appends a complete Turn (including Elapsed and any other fields)
// to the session and saves immediately.
func (s *Session) AppendTurn(turn Turn) {
func (s *Session) AppendTurn(turn Turn) error {
s.Turns = append(s.Turns, turn)
if err := s.Save(); err != nil {
sessionLog.Log("save failed: %v", err)
return err
}
return nil
}
Loading