@ottocode/server 0.1.260 → 0.1.262

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/package.json +4 -3
  2. package/src/index.ts +5 -4
  3. package/src/openapi/register.ts +92 -0
  4. package/src/openapi/route.ts +22 -0
  5. package/src/routes/ask.ts +210 -99
  6. package/src/routes/auth.ts +1701 -626
  7. package/src/routes/branch.ts +281 -90
  8. package/src/routes/config/agents.ts +79 -32
  9. package/src/routes/config/cwd.ts +46 -14
  10. package/src/routes/config/debug.ts +159 -30
  11. package/src/routes/config/defaults.ts +182 -64
  12. package/src/routes/config/main.ts +109 -73
  13. package/src/routes/config/models.ts +304 -137
  14. package/src/routes/config/providers.ts +462 -166
  15. package/src/routes/config/utils.ts +2 -2
  16. package/src/routes/doctor.ts +395 -161
  17. package/src/routes/files.ts +650 -260
  18. package/src/routes/git/branch.ts +143 -52
  19. package/src/routes/git/commit.ts +347 -141
  20. package/src/routes/git/diff.ts +239 -116
  21. package/src/routes/git/init.ts +103 -23
  22. package/src/routes/git/pull.ts +167 -65
  23. package/src/routes/git/push.ts +222 -117
  24. package/src/routes/git/remote.ts +401 -100
  25. package/src/routes/git/staging.ts +502 -141
  26. package/src/routes/git/status.ts +171 -78
  27. package/src/routes/mcp.ts +1129 -404
  28. package/src/routes/openapi.ts +27 -4
  29. package/src/routes/ottorouter.ts +1221 -389
  30. package/src/routes/provider-usage.ts +153 -36
  31. package/src/routes/research.ts +817 -370
  32. package/src/routes/root.ts +50 -6
  33. package/src/routes/session-approval.ts +228 -54
  34. package/src/routes/session-files.ts +265 -134
  35. package/src/routes/session-messages.ts +330 -150
  36. package/src/routes/session-stream.ts +83 -2
  37. package/src/routes/sessions.ts +1830 -780
  38. package/src/routes/skills.ts +849 -161
  39. package/src/routes/terminals.ts +469 -103
  40. package/src/routes/tunnel.ts +394 -118
  41. package/src/runtime/ask/service.ts +1 -0
  42. package/src/runtime/message/compaction-limits.ts +3 -3
  43. package/src/runtime/provider/reasoning.ts +2 -1
  44. package/src/runtime/session/db-operations.ts +4 -3
  45. package/src/runtime/utils/token.ts +7 -2
  46. package/src/tools/adapter.ts +21 -0
  47. package/src/openapi/paths/ask.ts +0 -81
  48. package/src/openapi/paths/auth.ts +0 -687
  49. package/src/openapi/paths/branch.ts +0 -102
  50. package/src/openapi/paths/config.ts +0 -485
  51. package/src/openapi/paths/doctor.ts +0 -165
  52. package/src/openapi/paths/files.ts +0 -236
  53. package/src/openapi/paths/git.ts +0 -690
  54. package/src/openapi/paths/mcp.ts +0 -339
  55. package/src/openapi/paths/messages.ts +0 -103
  56. package/src/openapi/paths/ottorouter.ts +0 -594
  57. package/src/openapi/paths/provider-usage.ts +0 -59
  58. package/src/openapi/paths/research.ts +0 -227
  59. package/src/openapi/paths/session-approval.ts +0 -93
  60. package/src/openapi/paths/session-extras.ts +0 -336
  61. package/src/openapi/paths/session-files.ts +0 -91
  62. package/src/openapi/paths/sessions.ts +0 -210
  63. package/src/openapi/paths/skills.ts +0 -377
  64. package/src/openapi/paths/stream.ts +0 -26
  65. package/src/openapi/paths/terminals.ts +0 -226
  66. package/src/openapi/paths/tunnel.ts +0 -163
  67. package/src/openapi/spec.ts +0 -73
@@ -19,6 +19,7 @@ import {
19
19
  getDefault,
20
20
  getProviderDetails,
21
21
  } from './utils.ts';
22
+ import { openApiRoute } from '../../openapi/route.ts';
22
23
 
23
24
  type ProviderMutationBody = {
24
25
  enabled?: boolean;
@@ -53,191 +54,486 @@ function toDiscoveredModel(model: ModelInfo) {
53
54
  }
54
55
 
55
56
  export function registerProvidersRoute(app: Hono) {
56
- app.get('/v1/config/providers', async (c) => {
57
- try {
58
- const embeddedConfig = (
59
- c as unknown as {
60
- get: (key: 'embeddedConfig') => EmbeddedAppConfig | undefined;
57
+ openApiRoute(
58
+ app,
59
+ {
60
+ method: 'get',
61
+ path: '/v1/config/providers',
62
+ tags: ['config'],
63
+ operationId: 'getProviders',
64
+ summary: 'Get available providers',
65
+ description: 'Returns only providers that have been authorized',
66
+ parameters: [
67
+ {
68
+ in: 'query',
69
+ name: 'project',
70
+ required: false,
71
+ schema: {
72
+ type: 'string',
73
+ },
74
+ description:
75
+ 'Project root override (defaults to current working directory).',
76
+ },
77
+ ],
78
+ responses: {
79
+ '200': {
80
+ description: 'OK',
81
+ content: {
82
+ 'application/json': {
83
+ schema: {
84
+ type: 'object',
85
+ properties: {
86
+ providers: {
87
+ type: 'array',
88
+ items: {
89
+ $ref: '#/components/schemas/Provider',
90
+ },
91
+ },
92
+ details: {
93
+ type: 'array',
94
+ items: {
95
+ $ref: '#/components/schemas/ProviderDetail',
96
+ },
97
+ },
98
+ default: {
99
+ $ref: '#/components/schemas/Provider',
100
+ },
101
+ },
102
+ required: ['providers', 'details', 'default'],
103
+ },
104
+ },
105
+ },
106
+ },
107
+ },
108
+ },
109
+ async (c) => {
110
+ try {
111
+ const embeddedConfig = (
112
+ c as unknown as {
113
+ get: (key: 'embeddedConfig') => EmbeddedAppConfig | undefined;
114
+ }
115
+ ).get('embeddedConfig');
116
+
117
+ if (embeddedConfig && Object.keys(embeddedConfig).length > 0) {
118
+ const providers = embeddedConfig.auth
119
+ ? (Object.keys(embeddedConfig.auth) as ProviderId[])
120
+ : embeddedConfig.provider
121
+ ? [embeddedConfig.provider]
122
+ : [];
123
+
124
+ return c.json({
125
+ providers,
126
+ details: providers.map((provider) => ({
127
+ id: provider,
128
+ label: provider,
129
+ source: 'built-in',
130
+ enabled: true,
131
+ authorized: true,
132
+ custom: false,
133
+ hasApiKey: false,
134
+ allowAnyModel: false,
135
+ modelCount: 0,
136
+ })),
137
+ default: getDefault(
138
+ embeddedConfig.provider,
139
+ embeddedConfig.defaults?.provider,
140
+ undefined,
141
+ ),
142
+ });
61
143
  }
62
- ).get('embeddedConfig');
63
144
 
64
- if (embeddedConfig && Object.keys(embeddedConfig).length > 0) {
65
- const providers = embeddedConfig.auth
66
- ? (Object.keys(embeddedConfig.auth) as ProviderId[])
67
- : embeddedConfig.provider
68
- ? [embeddedConfig.provider]
69
- : [];
145
+ const projectRoot = c.req.query('project') || process.cwd();
146
+ const cfg = await loadConfig(projectRoot);
147
+
148
+ const authorizedProviders = await getAuthorizedProviders(
149
+ undefined,
150
+ cfg,
151
+ );
152
+ const details = await getProviderDetails(undefined, cfg);
70
153
 
71
154
  return c.json({
72
- providers,
73
- details: providers.map((provider) => ({
74
- id: provider,
75
- label: provider,
76
- source: 'built-in',
77
- enabled: true,
78
- authorized: true,
79
- custom: false,
80
- hasApiKey: false,
81
- allowAnyModel: false,
82
- modelCount: 0,
83
- })),
84
- default: getDefault(
85
- embeddedConfig.provider,
86
- embeddedConfig.defaults?.provider,
87
- undefined,
88
- ),
155
+ providers: authorizedProviders,
156
+ details,
157
+ default: cfg.defaults.provider,
89
158
  });
159
+ } catch (error) {
160
+ logger.error('Failed to get providers', error);
161
+ const errorResponse = serializeError(error);
162
+ return c.json(errorResponse, errorResponse.error.status || 500);
90
163
  }
164
+ },
165
+ );
91
166
 
92
- const projectRoot = c.req.query('project') || process.cwd();
93
- const cfg = await loadConfig(projectRoot);
94
-
95
- const authorizedProviders = await getAuthorizedProviders(undefined, cfg);
96
- const details = await getProviderDetails(undefined, cfg);
97
-
98
- return c.json({
99
- providers: authorizedProviders,
100
- details,
101
- default: cfg.defaults.provider,
102
- });
103
- } catch (error) {
104
- logger.error('Failed to get providers', error);
105
- const errorResponse = serializeError(error);
106
- return c.json(errorResponse, errorResponse.error.status || 500);
107
- }
108
- });
109
-
110
- app.post('/v1/config/providers/discover-models', async (c) => {
111
- try {
112
- const embeddedConfig = (
113
- c as unknown as {
114
- get: (key: 'embeddedConfig') => EmbeddedAppConfig | undefined;
167
+ openApiRoute(
168
+ app,
169
+ {
170
+ method: 'post',
171
+ path: '/v1/config/providers/discover-models',
172
+ tags: ['config'],
173
+ operationId: 'discoverProviderModels',
174
+ summary: 'Discover models for a provider',
175
+ description:
176
+ 'Discovers available models from a provider base URL. Currently supports Ollama-compatible providers.',
177
+ requestBody: {
178
+ required: true,
179
+ content: {
180
+ 'application/json': {
181
+ schema: {
182
+ type: 'object',
183
+ properties: {
184
+ compatibility: {
185
+ type: 'string',
186
+ description:
187
+ 'Provider compatibility mode. Model discovery currently supports ollama.',
188
+ },
189
+ baseURL: {
190
+ type: 'string',
191
+ description: 'Provider base URL to inspect.',
192
+ },
193
+ apiKey: {
194
+ type: 'string',
195
+ description: 'Optional API key for the provider.',
196
+ },
197
+ },
198
+ required: ['baseURL'],
199
+ },
200
+ },
201
+ },
202
+ },
203
+ responses: {
204
+ '200': {
205
+ description: 'Discovered provider models',
206
+ content: {
207
+ 'application/json': {
208
+ schema: {
209
+ type: 'object',
210
+ properties: {
211
+ baseURL: { type: 'string' },
212
+ models: {
213
+ type: 'array',
214
+ items: {
215
+ type: 'object',
216
+ properties: {
217
+ id: { type: 'string' },
218
+ label: { type: 'string' },
219
+ toolCall: { type: 'boolean' },
220
+ reasoningText: { type: 'boolean' },
221
+ vision: { type: 'boolean' },
222
+ attachment: { type: 'boolean' },
223
+ contextWindow: { type: 'number' },
224
+ maxOutputTokens: { type: 'number' },
225
+ },
226
+ required: ['id', 'label'],
227
+ },
228
+ },
229
+ unsupported: { type: 'boolean' },
230
+ message: { type: 'string' },
231
+ },
232
+ required: ['models'],
233
+ },
234
+ },
235
+ },
236
+ },
237
+ '400': { description: 'Invalid discovery request' },
238
+ },
239
+ },
240
+ async (c) => {
241
+ try {
242
+ const embeddedConfig = (
243
+ c as unknown as {
244
+ get: (key: 'embeddedConfig') => EmbeddedAppConfig | undefined;
245
+ }
246
+ ).get('embeddedConfig');
247
+ if (embeddedConfig && Object.keys(embeddedConfig).length > 0) {
248
+ return c.json({ error: 'Embedded config cannot be modified' }, 400);
249
+ }
250
+
251
+ const body = await c.req.json<ProviderDiscoveryBody>();
252
+ const compatibility = body.compatibility || 'openai-compatible';
253
+ const baseURL = body.baseURL?.trim();
254
+ const apiKey = body.apiKey?.trim() || undefined;
255
+ if (!baseURL) return c.json({ error: 'Base URL is required' }, 400);
256
+
257
+ if (compatibility !== 'ollama') {
258
+ return c.json({
259
+ models: [],
260
+ unsupported: true,
261
+ message:
262
+ 'Model discovery is currently available for Ollama providers.',
263
+ });
115
264
  }
116
- ).get('embeddedConfig');
117
- if (embeddedConfig && Object.keys(embeddedConfig).length > 0) {
118
- return c.json({ error: 'Embedded config cannot be modified' }, 400);
119
- }
120
265
 
121
- const body = await c.req.json<ProviderDiscoveryBody>();
122
- const compatibility = body.compatibility || 'openai-compatible';
123
- const baseURL = body.baseURL?.trim();
124
- const apiKey = body.apiKey?.trim() || undefined;
125
- if (!baseURL) return c.json({ error: 'Base URL is required' }, 400);
266
+ const discovered = await discoverOllamaModels({
267
+ baseURL,
268
+ apiKey,
269
+ includeDetails: true,
270
+ });
126
271
 
127
- if (compatibility !== 'ollama') {
128
272
  return c.json({
129
- models: [],
130
- unsupported: true,
131
- message:
132
- 'Model discovery is currently available for Ollama providers.',
273
+ baseURL: discovered.baseURL,
274
+ models: discovered.models.map(toDiscoveredModel),
133
275
  });
276
+ } catch (error) {
277
+ logger.error('Failed to discover provider models', error);
278
+ const errorResponse = serializeError(error);
279
+ return c.json(errorResponse, errorResponse.error.status || 500);
134
280
  }
281
+ },
282
+ );
135
283
 
136
- const discovered = await discoverOllamaModels({
137
- baseURL,
138
- apiKey,
139
- includeDetails: true,
140
- });
141
-
142
- return c.json({
143
- baseURL: discovered.baseURL,
144
- models: discovered.models.map(toDiscoveredModel),
145
- });
146
- } catch (error) {
147
- logger.error('Failed to discover provider models', error);
148
- const errorResponse = serializeError(error);
149
- return c.json(errorResponse, errorResponse.error.status || 500);
150
- }
151
- });
152
-
153
- app.put('/v1/config/providers/:provider', async (c) => {
154
- try {
155
- const embeddedConfig = (
156
- c as unknown as {
157
- get: (key: 'embeddedConfig') => EmbeddedAppConfig | undefined;
284
+ openApiRoute(
285
+ app,
286
+ {
287
+ method: 'put',
288
+ path: '/v1/config/providers/{provider}',
289
+ tags: ['config'],
290
+ operationId: 'updateProviderSettings',
291
+ summary: 'Create or update provider settings',
292
+ parameters: [
293
+ {
294
+ in: 'query',
295
+ name: 'project',
296
+ required: false,
297
+ schema: {
298
+ type: 'string',
299
+ },
300
+ description:
301
+ 'Project root override (defaults to current working directory).',
302
+ },
303
+ {
304
+ in: 'path',
305
+ name: 'provider',
306
+ required: true,
307
+ schema: {
308
+ $ref: '#/components/schemas/Provider',
309
+ },
310
+ },
311
+ ],
312
+ requestBody: {
313
+ required: true,
314
+ content: {
315
+ 'application/json': {
316
+ schema: {
317
+ type: 'object',
318
+ properties: {
319
+ enabled: {
320
+ type: 'boolean',
321
+ },
322
+ custom: {
323
+ type: 'boolean',
324
+ },
325
+ label: {
326
+ type: 'string',
327
+ },
328
+ compatibility: {
329
+ type: 'string',
330
+ },
331
+ family: {
332
+ type: 'string',
333
+ },
334
+ baseURL: {
335
+ type: 'string',
336
+ nullable: true,
337
+ },
338
+ apiKey: {
339
+ type: 'string',
340
+ nullable: true,
341
+ },
342
+ apiKeyEnv: {
343
+ type: 'string',
344
+ nullable: true,
345
+ },
346
+ models: {
347
+ type: 'array',
348
+ items: {
349
+ type: 'string',
350
+ },
351
+ },
352
+ allowAnyModel: {
353
+ type: 'boolean',
354
+ },
355
+ },
356
+ },
357
+ },
358
+ },
359
+ },
360
+ responses: {
361
+ '200': {
362
+ description: 'OK',
363
+ content: {
364
+ 'application/json': {
365
+ schema: {
366
+ type: 'object',
367
+ properties: {
368
+ success: {
369
+ type: 'boolean',
370
+ },
371
+ provider: {
372
+ $ref: '#/components/schemas/Provider',
373
+ },
374
+ details: {
375
+ type: 'array',
376
+ items: {
377
+ $ref: '#/components/schemas/ProviderDetail',
378
+ },
379
+ },
380
+ },
381
+ required: ['success', 'provider', 'details'],
382
+ },
383
+ },
384
+ },
385
+ },
386
+ },
387
+ },
388
+ async (c) => {
389
+ try {
390
+ const embeddedConfig = (
391
+ c as unknown as {
392
+ get: (key: 'embeddedConfig') => EmbeddedAppConfig | undefined;
393
+ }
394
+ ).get('embeddedConfig');
395
+ if (embeddedConfig && Object.keys(embeddedConfig).length > 0) {
396
+ return c.json({ error: 'Embedded config cannot be modified' }, 400);
158
397
  }
159
- ).get('embeddedConfig');
160
- if (embeddedConfig && Object.keys(embeddedConfig).length > 0) {
161
- return c.json({ error: 'Embedded config cannot be modified' }, 400);
162
- }
163
398
 
164
- const projectRoot = c.req.query('project') || process.cwd();
165
- const provider = c.req.param('provider').trim();
166
- const body = await c.req.json<ProviderMutationBody>();
167
- if (!provider) return c.json({ error: 'Provider is required' }, 400);
168
-
169
- const updates: ProviderSettingsEntry = {
170
- enabled: body.enabled ?? true,
171
- custom: isBuiltInProviderId(provider)
172
- ? body.custom
173
- : (body.custom ?? true),
174
- };
175
-
176
- if (body.label !== undefined)
177
- updates.label = body.label.trim() || undefined;
178
- if (body.compatibility !== undefined) {
179
- updates.compatibility = body.compatibility;
180
- }
181
- if (body.family !== undefined) updates.family = body.family;
182
- if (body.baseURL !== undefined) {
183
- updates.baseURL = body.baseURL?.trim() || undefined;
184
- }
185
- if (body.apiKey !== undefined)
186
- updates.apiKey = body.apiKey?.trim() || undefined;
187
- if (body.apiKeyEnv !== undefined) {
188
- updates.apiKeyEnv = body.apiKeyEnv?.trim() || undefined;
189
- }
190
- if (body.models !== undefined) {
191
- updates.models = body.models
192
- .map((model) => model.trim())
193
- .filter(Boolean);
194
- }
195
- if (body.allowAnyModel !== undefined) {
196
- updates.allowAnyModel = body.allowAnyModel;
197
- }
399
+ const projectRoot = c.req.query('project') || process.cwd();
400
+ const provider = c.req.param('provider').trim();
401
+ const body = await c.req.json<ProviderMutationBody>();
402
+ if (!provider) return c.json({ error: 'Provider is required' }, 400);
198
403
 
199
- if (!isBuiltInProviderId(provider) && !updates.compatibility) {
200
- return c.json({ error: 'Custom providers require compatibility' }, 400);
201
- }
404
+ const updates: ProviderSettingsEntry = {
405
+ enabled: body.enabled ?? true,
406
+ custom: isBuiltInProviderId(provider)
407
+ ? body.custom
408
+ : (body.custom ?? true),
409
+ };
410
+
411
+ if (body.label !== undefined)
412
+ updates.label = body.label.trim() || undefined;
413
+ if (body.compatibility !== undefined) {
414
+ updates.compatibility = body.compatibility;
415
+ }
416
+ if (body.family !== undefined) updates.family = body.family;
417
+ if (body.baseURL !== undefined) {
418
+ updates.baseURL = body.baseURL?.trim() || undefined;
419
+ }
420
+ if (body.apiKey !== undefined)
421
+ updates.apiKey = body.apiKey?.trim() || undefined;
422
+ if (body.apiKeyEnv !== undefined) {
423
+ updates.apiKeyEnv = body.apiKeyEnv?.trim() || undefined;
424
+ }
425
+ if (body.models !== undefined) {
426
+ updates.models = body.models
427
+ .map((model) => model.trim())
428
+ .filter(Boolean);
429
+ }
430
+ if (body.allowAnyModel !== undefined) {
431
+ updates.allowAnyModel = body.allowAnyModel;
432
+ }
202
433
 
203
- await writeProviderSettings('global', provider, updates, projectRoot);
204
- const cfg = await loadConfig(projectRoot);
205
- const details = await getProviderDetails(undefined, cfg);
206
- return c.json({
207
- success: true,
208
- provider,
209
- details,
210
- });
211
- } catch (error) {
212
- logger.error('Failed to update provider settings', error);
213
- const errorResponse = serializeError(error);
214
- return c.json(errorResponse, errorResponse.error.status || 500);
215
- }
216
- });
217
-
218
- app.delete('/v1/config/providers/:provider', async (c) => {
219
- try {
220
- const embeddedConfig = (
221
- c as unknown as {
222
- get: (key: 'embeddedConfig') => EmbeddedAppConfig | undefined;
434
+ if (!isBuiltInProviderId(provider) && !updates.compatibility) {
435
+ return c.json(
436
+ { error: 'Custom providers require compatibility' },
437
+ 400,
438
+ );
223
439
  }
224
- ).get('embeddedConfig');
225
- if (embeddedConfig && Object.keys(embeddedConfig).length > 0) {
226
- return c.json({ error: 'Embedded config cannot be modified' }, 400);
440
+
441
+ await writeProviderSettings('global', provider, updates, projectRoot);
442
+ const cfg = await loadConfig(projectRoot);
443
+ const details = await getProviderDetails(undefined, cfg);
444
+ return c.json({
445
+ success: true,
446
+ provider,
447
+ details,
448
+ });
449
+ } catch (error) {
450
+ logger.error('Failed to update provider settings', error);
451
+ const errorResponse = serializeError(error);
452
+ return c.json(errorResponse, errorResponse.error.status || 500);
227
453
  }
454
+ },
455
+ );
228
456
 
229
- const projectRoot = c.req.query('project') || process.cwd();
230
- const provider = c.req.param('provider').trim();
231
- if (!provider) return c.json({ error: 'Provider is required' }, 400);
232
-
233
- await removeProviderSettings('global', provider, projectRoot);
234
- const cfg = await loadConfig(projectRoot);
235
- const details = await getProviderDetails(undefined, cfg);
236
- return c.json({ success: true, provider, details });
237
- } catch (error) {
238
- logger.error('Failed to remove provider settings', error);
239
- const errorResponse = serializeError(error);
240
- return c.json(errorResponse, errorResponse.error.status || 500);
241
- }
242
- });
457
+ openApiRoute(
458
+ app,
459
+ {
460
+ method: 'delete',
461
+ path: '/v1/config/providers/{provider}',
462
+ tags: ['config'],
463
+ operationId: 'deleteProviderSettings',
464
+ summary: 'Delete provider override or custom provider entry',
465
+ parameters: [
466
+ {
467
+ in: 'query',
468
+ name: 'project',
469
+ required: false,
470
+ schema: {
471
+ type: 'string',
472
+ },
473
+ description:
474
+ 'Project root override (defaults to current working directory).',
475
+ },
476
+ {
477
+ in: 'path',
478
+ name: 'provider',
479
+ required: true,
480
+ schema: {
481
+ $ref: '#/components/schemas/Provider',
482
+ },
483
+ },
484
+ ],
485
+ responses: {
486
+ '200': {
487
+ description: 'OK',
488
+ content: {
489
+ 'application/json': {
490
+ schema: {
491
+ type: 'object',
492
+ properties: {
493
+ success: {
494
+ type: 'boolean',
495
+ },
496
+ provider: {
497
+ $ref: '#/components/schemas/Provider',
498
+ },
499
+ details: {
500
+ type: 'array',
501
+ items: {
502
+ $ref: '#/components/schemas/ProviderDetail',
503
+ },
504
+ },
505
+ },
506
+ required: ['success', 'provider', 'details'],
507
+ },
508
+ },
509
+ },
510
+ },
511
+ },
512
+ },
513
+ async (c) => {
514
+ try {
515
+ const embeddedConfig = (
516
+ c as unknown as {
517
+ get: (key: 'embeddedConfig') => EmbeddedAppConfig | undefined;
518
+ }
519
+ ).get('embeddedConfig');
520
+ if (embeddedConfig && Object.keys(embeddedConfig).length > 0) {
521
+ return c.json({ error: 'Embedded config cannot be modified' }, 400);
522
+ }
523
+
524
+ const projectRoot = c.req.query('project') || process.cwd();
525
+ const provider = c.req.param('provider').trim();
526
+ if (!provider) return c.json({ error: 'Provider is required' }, 400);
527
+
528
+ await removeProviderSettings('global', provider, projectRoot);
529
+ const cfg = await loadConfig(projectRoot);
530
+ const details = await getProviderDetails(undefined, cfg);
531
+ return c.json({ success: true, provider, details });
532
+ } catch (error) {
533
+ logger.error('Failed to remove provider settings', error);
534
+ const errorResponse = serializeError(error);
535
+ return c.json(errorResponse, errorResponse.error.status || 500);
536
+ }
537
+ },
538
+ );
243
539
  }
@@ -80,7 +80,7 @@ export async function getProviderDetails(
80
80
  ]),
81
81
  );
82
82
  const details = await Promise.all(
83
- providers.map(async (provider) => {
83
+ providers.map(async (provider): Promise<ProviderDetail | null> => {
84
84
  const definition = getProviderDefinition(fileConfig, provider);
85
85
  if (!definition) return null;
86
86
  const settings = getProviderSettings(fileConfig, provider);
@@ -112,7 +112,7 @@ export async function getProviderDetails(
112
112
  } satisfies ProviderDetail;
113
113
  }),
114
114
  );
115
- return details.filter((detail): detail is ProviderDetail => Boolean(detail));
115
+ return details.filter((detail) => detail !== null);
116
116
  }
117
117
 
118
118
  export function getDefault<T>(