1package server
2
3import (
4 "encoding/json"
5 "net/http"
6
7 "github.com/charmbracelet/crush/internal/config"
8 "github.com/charmbracelet/crush/internal/proto"
9)
10
11func (c *controllerV1) handlePostWorkspaceConfigSet(w http.ResponseWriter, r *http.Request) {
12 id := r.PathValue("id")
13
14 var req struct {
15 Scope config.Scope `json:"scope"`
16 Key string `json:"key"`
17 Value any `json:"value"`
18 }
19 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
20 c.server.logError(r, "Failed to decode request", "error", err)
21 jsonError(w, http.StatusBadRequest, "failed to decode request")
22 return
23 }
24
25 if err := c.backend.SetConfigField(id, req.Scope, req.Key, req.Value); err != nil {
26 c.handleError(w, r, err)
27 return
28 }
29 w.WriteHeader(http.StatusOK)
30}
31
32func (c *controllerV1) handlePostWorkspaceConfigRemove(w http.ResponseWriter, r *http.Request) {
33 id := r.PathValue("id")
34
35 var req struct {
36 Scope config.Scope `json:"scope"`
37 Key string `json:"key"`
38 }
39 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
40 c.server.logError(r, "Failed to decode request", "error", err)
41 jsonError(w, http.StatusBadRequest, "failed to decode request")
42 return
43 }
44
45 if err := c.backend.RemoveConfigField(id, req.Scope, req.Key); err != nil {
46 c.handleError(w, r, err)
47 return
48 }
49 w.WriteHeader(http.StatusOK)
50}
51
52func (c *controllerV1) handlePostWorkspaceConfigModel(w http.ResponseWriter, r *http.Request) {
53 id := r.PathValue("id")
54
55 var req struct {
56 Scope config.Scope `json:"scope"`
57 ModelType config.SelectedModelType `json:"model_type"`
58 Model config.SelectedModel `json:"model"`
59 }
60 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
61 c.server.logError(r, "Failed to decode request", "error", err)
62 jsonError(w, http.StatusBadRequest, "failed to decode request")
63 return
64 }
65
66 if err := c.backend.UpdatePreferredModel(id, req.Scope, req.ModelType, req.Model); err != nil {
67 c.handleError(w, r, err)
68 return
69 }
70 w.WriteHeader(http.StatusOK)
71}
72
73func (c *controllerV1) handlePostWorkspaceConfigCompact(w http.ResponseWriter, r *http.Request) {
74 id := r.PathValue("id")
75
76 var req struct {
77 Scope config.Scope `json:"scope"`
78 Enabled bool `json:"enabled"`
79 }
80 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
81 c.server.logError(r, "Failed to decode request", "error", err)
82 jsonError(w, http.StatusBadRequest, "failed to decode request")
83 return
84 }
85
86 if err := c.backend.SetCompactMode(id, req.Scope, req.Enabled); err != nil {
87 c.handleError(w, r, err)
88 return
89 }
90 w.WriteHeader(http.StatusOK)
91}
92
93func (c *controllerV1) handlePostWorkspaceConfigProviderKey(w http.ResponseWriter, r *http.Request) {
94 id := r.PathValue("id")
95
96 var req struct {
97 Scope config.Scope `json:"scope"`
98 ProviderID string `json:"provider_id"`
99 APIKey any `json:"api_key"`
100 }
101 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
102 c.server.logError(r, "Failed to decode request", "error", err)
103 jsonError(w, http.StatusBadRequest, "failed to decode request")
104 return
105 }
106
107 if err := c.backend.SetProviderAPIKey(id, req.Scope, req.ProviderID, req.APIKey); err != nil {
108 c.handleError(w, r, err)
109 return
110 }
111 w.WriteHeader(http.StatusOK)
112}
113
114func (c *controllerV1) handlePostWorkspaceConfigImportCopilot(w http.ResponseWriter, r *http.Request) {
115 id := r.PathValue("id")
116 token, ok, err := c.backend.ImportCopilot(id)
117 if err != nil {
118 c.handleError(w, r, err)
119 return
120 }
121 jsonEncode(w, struct {
122 Token any `json:"token"`
123 Success bool `json:"success"`
124 }{Token: token, Success: ok})
125}
126
127func (c *controllerV1) handlePostWorkspaceConfigRefreshOAuth(w http.ResponseWriter, r *http.Request) {
128 id := r.PathValue("id")
129
130 var req struct {
131 Scope config.Scope `json:"scope"`
132 ProviderID string `json:"provider_id"`
133 }
134 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
135 c.server.logError(r, "Failed to decode request", "error", err)
136 jsonError(w, http.StatusBadRequest, "failed to decode request")
137 return
138 }
139
140 if err := c.backend.RefreshOAuthToken(r.Context(), id, req.Scope, req.ProviderID); err != nil {
141 c.handleError(w, r, err)
142 return
143 }
144 w.WriteHeader(http.StatusOK)
145}
146
147func (c *controllerV1) handleGetWorkspaceProjectNeedsInit(w http.ResponseWriter, r *http.Request) {
148 id := r.PathValue("id")
149 needs, err := c.backend.ProjectNeedsInitialization(id)
150 if err != nil {
151 c.handleError(w, r, err)
152 return
153 }
154 jsonEncode(w, struct {
155 NeedsInit bool `json:"needs_init"`
156 }{NeedsInit: needs})
157}
158
159func (c *controllerV1) handlePostWorkspaceProjectInit(w http.ResponseWriter, r *http.Request) {
160 id := r.PathValue("id")
161 if err := c.backend.MarkProjectInitialized(id); err != nil {
162 c.handleError(w, r, err)
163 return
164 }
165 w.WriteHeader(http.StatusOK)
166}
167
168func (c *controllerV1) handleGetWorkspaceProjectInitPrompt(w http.ResponseWriter, r *http.Request) {
169 id := r.PathValue("id")
170 prompt, err := c.backend.InitializePrompt(id)
171 if err != nil {
172 c.handleError(w, r, err)
173 return
174 }
175 jsonEncode(w, struct {
176 Prompt string `json:"prompt"`
177 }{Prompt: prompt})
178}
179
180func (c *controllerV1) handlePostWorkspaceMCPRefreshTools(w http.ResponseWriter, r *http.Request) {
181 id := r.PathValue("id")
182
183 var req struct {
184 Name string `json:"name"`
185 }
186 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
187 c.server.logError(r, "Failed to decode request", "error", err)
188 jsonError(w, http.StatusBadRequest, "failed to decode request")
189 return
190 }
191
192 if err := c.backend.RefreshMCPTools(r.Context(), id, req.Name); err != nil {
193 c.handleError(w, r, err)
194 return
195 }
196 w.WriteHeader(http.StatusOK)
197}
198
199func (c *controllerV1) handlePostWorkspaceMCPReadResource(w http.ResponseWriter, r *http.Request) {
200 id := r.PathValue("id")
201
202 var req struct {
203 Name string `json:"name"`
204 URI string `json:"uri"`
205 }
206 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
207 c.server.logError(r, "Failed to decode request", "error", err)
208 jsonError(w, http.StatusBadRequest, "failed to decode request")
209 return
210 }
211
212 contents, err := c.backend.ReadMCPResource(r.Context(), id, req.Name, req.URI)
213 if err != nil {
214 c.handleError(w, r, err)
215 return
216 }
217 jsonEncode(w, contents)
218}
219
220func (c *controllerV1) handlePostWorkspaceMCPGetPrompt(w http.ResponseWriter, r *http.Request) {
221 id := r.PathValue("id")
222
223 var req struct {
224 ClientID string `json:"client_id"`
225 PromptID string `json:"prompt_id"`
226 Args map[string]string `json:"args"`
227 }
228 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
229 c.server.logError(r, "Failed to decode request", "error", err)
230 jsonError(w, http.StatusBadRequest, "failed to decode request")
231 return
232 }
233
234 prompt, err := c.backend.GetMCPPrompt(id, req.ClientID, req.PromptID, req.Args)
235 if err != nil {
236 c.handleError(w, r, err)
237 return
238 }
239 jsonEncode(w, struct {
240 Prompt string `json:"prompt"`
241 }{Prompt: prompt})
242}
243
244func (c *controllerV1) handleGetWorkspaceMCPStates(w http.ResponseWriter, r *http.Request) {
245 id := r.PathValue("id")
246 states := c.backend.MCPGetStates(id)
247 result := make(map[string]proto.MCPClientInfo, len(states))
248 for k, v := range states {
249 result[k] = proto.MCPClientInfo{
250 Name: v.Name,
251 State: proto.MCPState(v.State),
252 Error: v.Error,
253 ToolCount: v.Counts.Tools,
254 PromptCount: v.Counts.Prompts,
255 ResourceCount: v.Counts.Resources,
256 ConnectedAt: v.ConnectedAt,
257 }
258 }
259 jsonEncode(w, result)
260}
261
262func (c *controllerV1) handlePostWorkspaceMCPRefreshPrompts(w http.ResponseWriter, r *http.Request) {
263 id := r.PathValue("id")
264
265 var req struct {
266 Name string `json:"name"`
267 }
268 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
269 c.server.logError(r, "Failed to decode request", "error", err)
270 jsonError(w, http.StatusBadRequest, "failed to decode request")
271 return
272 }
273
274 c.backend.MCPRefreshPrompts(r.Context(), id, req.Name)
275 w.WriteHeader(http.StatusOK)
276}
277
278func (c *controllerV1) handlePostWorkspaceMCPRefreshResources(w http.ResponseWriter, r *http.Request) {
279 id := r.PathValue("id")
280
281 var req struct {
282 Name string `json:"name"`
283 }
284 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
285 c.server.logError(r, "Failed to decode request", "error", err)
286 jsonError(w, http.StatusBadRequest, "failed to decode request")
287 return
288 }
289
290 c.backend.MCPRefreshResources(r.Context(), id, req.Name)
291 w.WriteHeader(http.StatusOK)
292}