@@ -213,150 +213,7 @@ export async function handleCliSettingsRoutes(ctx: RouteContext): Promise<boolea
213213 return true ;
214214 }
215215
216- // ========== GET SINGLE SETTINGS ==========
217- // GET /api/cli/settings/:id
218- const getMatch = pathname . match ( / ^ \/ a p i \/ c l i \/ s e t t i n g s \/ ( [ ^ / ] + ) $ / ) ;
219- if ( getMatch && req . method === 'GET' ) {
220- const endpointId = sanitizeEndpointId ( getMatch [ 1 ] ) ;
221- try {
222- const endpoint = loadEndpointSettings ( endpointId ) ;
223-
224- if ( ! endpoint ) {
225- res . writeHead ( 404 , { 'Content-Type' : 'application/json' } ) ;
226- res . end ( JSON . stringify ( { error : 'Endpoint not found' } ) ) ;
227- return true ;
228- }
229-
230- res . writeHead ( 200 , { 'Content-Type' : 'application/json' } ) ;
231- res . end ( JSON . stringify ( {
232- endpoint,
233- filePath : getSettingsFilePath ( endpointId )
234- } ) ) ;
235- } catch ( err ) {
236- res . writeHead ( 500 , { 'Content-Type' : 'application/json' } ) ;
237- res . end ( JSON . stringify ( { error : ( err as Error ) . message } ) ) ;
238- }
239- return true ;
240- }
241-
242- // ========== UPDATE SETTINGS ==========
243- // PUT /api/cli/settings/:id
244- const putMatch = pathname . match ( / ^ \/ a p i \/ c l i \/ s e t t i n g s \/ ( [ ^ / ] + ) $ / ) ;
245- if ( putMatch && req . method === 'PUT' ) {
246- const endpointId = sanitizeEndpointId ( putMatch [ 1 ] ) ;
247- handlePostRequest ( req , res , async ( body : unknown ) => {
248- try {
249- const request = body as Partial < SaveEndpointRequest > ;
250-
251- // Check if just toggling enabled status
252- if ( Object . keys ( request ) . length === 1 && 'enabled' in request ) {
253- const result = toggleEndpointEnabled ( endpointId , request . enabled as boolean ) ;
254-
255- if ( result . success ) {
256- broadcastToClients ( {
257- type : 'CLI_SETTINGS_TOGGLED' ,
258- payload : {
259- endpointId,
260- enabled : request . enabled ,
261- timestamp : new Date ( ) . toISOString ( )
262- }
263- } ) ;
264- }
265- return result ;
266- }
267-
268- // Full update
269- const existing = loadEndpointSettings ( endpointId ) ;
270- if ( ! existing ) {
271- return { error : 'Endpoint not found' , status : 404 } ;
272- }
273-
274- const updateRequest : SaveEndpointRequest = {
275- id : endpointId ,
276- name : request . name || existing . name ,
277- description : request . description ?? existing . description ,
278- settings : request . settings || existing . settings ,
279- enabled : request . enabled ?? existing . enabled
280- } ;
281-
282- const result = saveEndpointSettings ( updateRequest ) ;
283-
284- if ( result . success ) {
285- broadcastToClients ( {
286- type : 'CLI_SETTINGS_UPDATED' ,
287- payload : {
288- endpoint : result . endpoint ,
289- filePath : result . filePath ,
290- timestamp : new Date ( ) . toISOString ( )
291- }
292- } ) ;
293- }
294-
295- return result ;
296- } catch ( err ) {
297- return { error : ( err as Error ) . message , status : 500 } ;
298- }
299- } ) ;
300- return true ;
301- }
302-
303- // ========== DELETE SETTINGS ==========
304- // DELETE /api/cli/settings/:id
305- const deleteMatch = pathname . match ( / ^ \/ a p i \/ c l i \/ s e t t i n g s \/ ( [ ^ / ] + ) $ / ) ;
306- if ( deleteMatch && req . method === 'DELETE' ) {
307- const endpointId = sanitizeEndpointId ( deleteMatch [ 1 ] ) ;
308- try {
309- const result = deleteEndpointSettings ( endpointId ) ;
310-
311- if ( result . success ) {
312- broadcastToClients ( {
313- type : 'CLI_SETTINGS_DELETED' ,
314- payload : {
315- endpointId,
316- timestamp : new Date ( ) . toISOString ( )
317- }
318- } ) ;
319-
320- res . writeHead ( 200 , { 'Content-Type' : 'application/json' } ) ;
321- res . end ( JSON . stringify ( result ) ) ;
322- } else {
323- res . writeHead ( 404 , { 'Content-Type' : 'application/json' } ) ;
324- res . end ( JSON . stringify ( result ) ) ;
325- }
326- } catch ( err ) {
327- res . writeHead ( 500 , { 'Content-Type' : 'application/json' } ) ;
328- res . end ( JSON . stringify ( { error : ( err as Error ) . message } ) ) ;
329- }
330- return true ;
331- }
332-
333- // ========== GET SETTINGS FILE PATH ==========
334- // GET /api/cli/settings/:id/path
335- const pathMatch = pathname . match ( / ^ \/ a p i \/ c l i \/ s e t t i n g s \/ ( [ ^ / ] + ) \/ p a t h $ / ) ;
336- if ( pathMatch && req . method === 'GET' ) {
337- const endpointId = sanitizeEndpointId ( pathMatch [ 1 ] ) ;
338- try {
339- const endpoint = loadEndpointSettings ( endpointId ) ;
340-
341- if ( ! endpoint ) {
342- res . writeHead ( 404 , { 'Content-Type' : 'application/json' } ) ;
343- res . end ( JSON . stringify ( { error : 'Endpoint not found' } ) ) ;
344- return true ;
345- }
346-
347- const filePath = getSettingsFilePath ( endpointId ) ;
348- res . writeHead ( 200 , { 'Content-Type' : 'application/json' } ) ;
349- res . end ( JSON . stringify ( {
350- endpointId,
351- filePath,
352- enabled : endpoint . enabled
353- } ) ) ;
354- } catch ( err ) {
355- res . writeHead ( 500 , { 'Content-Type' : 'application/json' } ) ;
356- res . end ( JSON . stringify ( { error : ( err as Error ) . message } ) ) ;
357- }
358- return true ;
359- }
216+ // ========== NAMED SUB-ROUTES (must come before :id routes) ==========
360217
361218 // ========== SYNC BUILTIN TOOLS AVAILABILITY ==========
362219 // POST /api/cli/settings/sync-tools
@@ -505,5 +362,152 @@ export async function handleCliSettingsRoutes(ctx: RouteContext): Promise<boolea
505362 return true ;
506363 }
507364
365+ // ========== PARAMETERIZED :id ROUTES ==========
366+
367+ // ========== GET SINGLE SETTINGS ==========
368+ // GET /api/cli/settings/:id
369+ const getMatch = pathname . match ( / ^ \/ a p i \/ c l i \/ s e t t i n g s \/ ( [ ^ / ] + ) $ / ) ;
370+ if ( getMatch && req . method === 'GET' ) {
371+ const endpointId = sanitizeEndpointId ( getMatch [ 1 ] ) ;
372+ try {
373+ const endpoint = loadEndpointSettings ( endpointId ) ;
374+
375+ if ( ! endpoint ) {
376+ res . writeHead ( 404 , { 'Content-Type' : 'application/json' } ) ;
377+ res . end ( JSON . stringify ( { error : 'Endpoint not found' } ) ) ;
378+ return true ;
379+ }
380+
381+ res . writeHead ( 200 , { 'Content-Type' : 'application/json' } ) ;
382+ res . end ( JSON . stringify ( {
383+ endpoint,
384+ filePath : getSettingsFilePath ( endpointId )
385+ } ) ) ;
386+ } catch ( err ) {
387+ res . writeHead ( 500 , { 'Content-Type' : 'application/json' } ) ;
388+ res . end ( JSON . stringify ( { error : ( err as Error ) . message } ) ) ;
389+ }
390+ return true ;
391+ }
392+
393+ // ========== UPDATE SETTINGS ==========
394+ // PUT /api/cli/settings/:id
395+ const putMatch = pathname . match ( / ^ \/ a p i \/ c l i \/ s e t t i n g s \/ ( [ ^ / ] + ) $ / ) ;
396+ if ( putMatch && req . method === 'PUT' ) {
397+ const endpointId = sanitizeEndpointId ( putMatch [ 1 ] ) ;
398+ handlePostRequest ( req , res , async ( body : unknown ) => {
399+ try {
400+ const request = body as Partial < SaveEndpointRequest > ;
401+
402+ // Check if just toggling enabled status
403+ if ( Object . keys ( request ) . length === 1 && 'enabled' in request ) {
404+ const result = toggleEndpointEnabled ( endpointId , request . enabled as boolean ) ;
405+
406+ if ( result . success ) {
407+ broadcastToClients ( {
408+ type : 'CLI_SETTINGS_TOGGLED' ,
409+ payload : {
410+ endpointId,
411+ enabled : request . enabled ,
412+ timestamp : new Date ( ) . toISOString ( )
413+ }
414+ } ) ;
415+ }
416+ return result ;
417+ }
418+
419+ // Full update
420+ const existing = loadEndpointSettings ( endpointId ) ;
421+ if ( ! existing ) {
422+ return { error : 'Endpoint not found' , status : 404 } ;
423+ }
424+
425+ const updateRequest : SaveEndpointRequest = {
426+ id : endpointId ,
427+ name : request . name || existing . name ,
428+ description : request . description ?? existing . description ,
429+ settings : request . settings || existing . settings ,
430+ enabled : request . enabled ?? existing . enabled
431+ } ;
432+
433+ const result = saveEndpointSettings ( updateRequest ) ;
434+
435+ if ( result . success ) {
436+ broadcastToClients ( {
437+ type : 'CLI_SETTINGS_UPDATED' ,
438+ payload : {
439+ endpoint : result . endpoint ,
440+ filePath : result . filePath ,
441+ timestamp : new Date ( ) . toISOString ( )
442+ }
443+ } ) ;
444+ }
445+
446+ return result ;
447+ } catch ( err ) {
448+ return { error : ( err as Error ) . message , status : 500 } ;
449+ }
450+ } ) ;
451+ return true ;
452+ }
453+
454+ // ========== DELETE SETTINGS ==========
455+ // DELETE /api/cli/settings/:id
456+ const deleteMatch = pathname . match ( / ^ \/ a p i \/ c l i \/ s e t t i n g s \/ ( [ ^ / ] + ) $ / ) ;
457+ if ( deleteMatch && req . method === 'DELETE' ) {
458+ const endpointId = sanitizeEndpointId ( deleteMatch [ 1 ] ) ;
459+ try {
460+ const result = deleteEndpointSettings ( endpointId ) ;
461+
462+ if ( result . success ) {
463+ broadcastToClients ( {
464+ type : 'CLI_SETTINGS_DELETED' ,
465+ payload : {
466+ endpointId,
467+ timestamp : new Date ( ) . toISOString ( )
468+ }
469+ } ) ;
470+
471+ res . writeHead ( 200 , { 'Content-Type' : 'application/json' } ) ;
472+ res . end ( JSON . stringify ( result ) ) ;
473+ } else {
474+ res . writeHead ( 404 , { 'Content-Type' : 'application/json' } ) ;
475+ res . end ( JSON . stringify ( result ) ) ;
476+ }
477+ } catch ( err ) {
478+ res . writeHead ( 500 , { 'Content-Type' : 'application/json' } ) ;
479+ res . end ( JSON . stringify ( { error : ( err as Error ) . message } ) ) ;
480+ }
481+ return true ;
482+ }
483+
484+ // ========== GET SETTINGS FILE PATH ==========
485+ // GET /api/cli/settings/:id/path
486+ const pathMatch = pathname . match ( / ^ \/ a p i \/ c l i \/ s e t t i n g s \/ ( [ ^ / ] + ) \/ p a t h $ / ) ;
487+ if ( pathMatch && req . method === 'GET' ) {
488+ const endpointId = sanitizeEndpointId ( pathMatch [ 1 ] ) ;
489+ try {
490+ const endpoint = loadEndpointSettings ( endpointId ) ;
491+
492+ if ( ! endpoint ) {
493+ res . writeHead ( 404 , { 'Content-Type' : 'application/json' } ) ;
494+ res . end ( JSON . stringify ( { error : 'Endpoint not found' } ) ) ;
495+ return true ;
496+ }
497+
498+ const filePath = getSettingsFilePath ( endpointId ) ;
499+ res . writeHead ( 200 , { 'Content-Type' : 'application/json' } ) ;
500+ res . end ( JSON . stringify ( {
501+ endpointId,
502+ filePath,
503+ enabled : endpoint . enabled
504+ } ) ) ;
505+ } catch ( err ) {
506+ res . writeHead ( 500 , { 'Content-Type' : 'application/json' } ) ;
507+ res . end ( JSON . stringify ( { error : ( err as Error ) . message } ) ) ;
508+ }
509+ return true ;
510+ }
511+
508512 return false ;
509513}
0 commit comments