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