config.go

  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}