proto.go

  1package proto
  2
  3import (
  4	"encoding/json"
  5	"errors"
  6	"time"
  7
  8	"charm.land/catwalk/pkg/catwalk"
  9	"github.com/charmbracelet/crush/internal/config"
 10	"github.com/charmbracelet/crush/internal/lsp"
 11)
 12
 13// Workspace represents a running app.App workspace with its associated
 14// resources and state.
 15type Workspace struct {
 16	ID       string         `json:"id"`
 17	Path     string         `json:"path"`
 18	YOLO     bool           `json:"yolo,omitempty"`
 19	Debug    bool           `json:"debug,omitempty"`
 20	DataDir  string         `json:"data_dir,omitempty"`
 21	Version  string         `json:"version,omitempty"`
 22	ClientID string         `json:"client_id,omitempty"`
 23	Config   *config.Config `json:"config,omitempty"`
 24	Env      []string       `json:"env,omitempty"`
 25}
 26
 27// Error represents an error response.
 28type Error struct {
 29	Message string `json:"message"`
 30}
 31
 32// ConfigChanged is published whenever the workspace's configuration is
 33// mutated by a backend operation. Clients react by re-fetching the
 34// workspace snapshot so cached config stays in sync across subscribers.
 35type ConfigChanged struct {
 36	WorkspaceID string `json:"workspace_id"`
 37}
 38
 39// CurrentSession is the request body for the per-client
 40// current-session endpoint. An empty SessionID clears the entry.
 41type CurrentSession struct {
 42	SessionID string `json:"session_id"`
 43}
 44
 45// AgentInfo represents information about the agent.
 46type AgentInfo struct {
 47	IsBusy   bool                 `json:"is_busy"`
 48	IsReady  bool                 `json:"is_ready"`
 49	Model    catwalk.Model        `json:"model"`
 50	ModelCfg config.SelectedModel `json:"model_cfg"`
 51}
 52
 53// IsZero checks if the AgentInfo is zero-valued.
 54func (a AgentInfo) IsZero() bool {
 55	return !a.IsBusy && !a.IsReady && a.Model.ID == ""
 56}
 57
 58// AgentMessage represents a message sent to the agent.
 59type AgentMessage struct {
 60	SessionID   string       `json:"session_id"`
 61	Prompt      string       `json:"prompt"`
 62	Attachments []Attachment `json:"attachments,omitempty"`
 63}
 64
 65// AgentSession represents a session with its busy status.
 66type AgentSession struct {
 67	Session
 68	IsBusy bool `json:"is_busy"`
 69}
 70
 71// IsZero checks if the AgentSession is zero-valued.
 72func (a AgentSession) IsZero() bool {
 73	return a.ID == "" && !a.IsBusy
 74}
 75
 76// PermissionAction represents an action taken on a permission request.
 77type PermissionAction string
 78
 79const (
 80	PermissionAllow           PermissionAction = "allow"
 81	PermissionAllowForSession PermissionAction = "allow_session"
 82	PermissionDeny            PermissionAction = "deny"
 83)
 84
 85// MarshalText implements the [encoding.TextMarshaler] interface.
 86func (p PermissionAction) MarshalText() ([]byte, error) {
 87	return []byte(p), nil
 88}
 89
 90// UnmarshalText implements the [encoding.TextUnmarshaler] interface.
 91func (p *PermissionAction) UnmarshalText(text []byte) error {
 92	*p = PermissionAction(text)
 93	return nil
 94}
 95
 96// PermissionGrant represents a permission grant request.
 97type PermissionGrant struct {
 98	Permission PermissionRequest `json:"permission"`
 99	Action     PermissionAction  `json:"action"`
100}
101
102// PermissionGrantResponse is the server's response to a permission
103// grant call. Resolved is true when this call resolved the pending
104// request, and false when the request had already been resolved by a
105// previous caller (e.g., another client in a multi-subscriber UI). A
106// false value is not an error.
107type PermissionGrantResponse struct {
108	Resolved bool `json:"resolved"`
109}
110
111// PermissionSkipRequest represents a request to skip permission prompts.
112type PermissionSkipRequest struct {
113	Skip bool `json:"skip"`
114}
115
116// LSPEventType represents the type of LSP event.
117type LSPEventType string
118
119const (
120	LSPEventStateChanged       LSPEventType = "state_changed"
121	LSPEventDiagnosticsChanged LSPEventType = "diagnostics_changed"
122)
123
124// MarshalText implements the [encoding.TextMarshaler] interface.
125func (e LSPEventType) MarshalText() ([]byte, error) {
126	return []byte(e), nil
127}
128
129// UnmarshalText implements the [encoding.TextUnmarshaler] interface.
130func (e *LSPEventType) UnmarshalText(data []byte) error {
131	*e = LSPEventType(data)
132	return nil
133}
134
135// LSPEvent represents an event in the LSP system.
136type LSPEvent struct {
137	Type            LSPEventType    `json:"type"`
138	Name            string          `json:"name"`
139	State           lsp.ServerState `json:"state"`
140	Error           error           `json:"error,omitempty"`
141	DiagnosticCount int             `json:"diagnostic_count,omitempty"`
142}
143
144// MarshalJSON implements the [json.Marshaler] interface.
145func (e LSPEvent) MarshalJSON() ([]byte, error) {
146	type Alias LSPEvent
147	return json.Marshal(&struct {
148		Error string `json:"error,omitempty"`
149		Alias
150	}{
151		Error: func() string {
152			if e.Error != nil {
153				return e.Error.Error()
154			}
155			return ""
156		}(),
157		Alias: Alias(e),
158	})
159}
160
161// UnmarshalJSON implements the [json.Unmarshaler] interface.
162func (e *LSPEvent) UnmarshalJSON(data []byte) error {
163	type Alias LSPEvent
164	aux := &struct {
165		Error string `json:"error,omitempty"`
166		Alias
167	}{
168		Alias: Alias(*e),
169	}
170	if err := json.Unmarshal(data, &aux); err != nil {
171		return err
172	}
173	*e = LSPEvent(aux.Alias)
174	if aux.Error != "" {
175		e.Error = errors.New(aux.Error)
176	}
177	return nil
178}
179
180// LSPClientInfo holds information about an LSP client's state.
181type LSPClientInfo struct {
182	Name            string          `json:"name"`
183	State           lsp.ServerState `json:"state"`
184	Error           error           `json:"error,omitempty"`
185	DiagnosticCount int             `json:"diagnostic_count,omitempty"`
186	ConnectedAt     time.Time       `json:"connected_at"`
187}
188
189// MarshalJSON implements the [json.Marshaler] interface.
190func (i LSPClientInfo) MarshalJSON() ([]byte, error) {
191	type Alias LSPClientInfo
192	return json.Marshal(&struct {
193		Error string `json:"error,omitempty"`
194		Alias
195	}{
196		Error: func() string {
197			if i.Error != nil {
198				return i.Error.Error()
199			}
200			return ""
201		}(),
202		Alias: Alias(i),
203	})
204}
205
206// UnmarshalJSON implements the [json.Unmarshaler] interface.
207func (i *LSPClientInfo) UnmarshalJSON(data []byte) error {
208	type Alias LSPClientInfo
209	aux := &struct {
210		Error string `json:"error,omitempty"`
211		Alias
212	}{
213		Alias: Alias(*i),
214	}
215	if err := json.Unmarshal(data, &aux); err != nil {
216		return err
217	}
218	*i = LSPClientInfo(aux.Alias)
219	if aux.Error != "" {
220		i.Error = errors.New(aux.Error)
221	}
222	return nil
223}