@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.
- package/package.json +4 -3
- package/src/index.ts +5 -4
- package/src/openapi/register.ts +92 -0
- package/src/openapi/route.ts +22 -0
- package/src/routes/ask.ts +210 -99
- package/src/routes/auth.ts +1701 -626
- package/src/routes/branch.ts +281 -90
- package/src/routes/config/agents.ts +79 -32
- package/src/routes/config/cwd.ts +46 -14
- package/src/routes/config/debug.ts +159 -30
- package/src/routes/config/defaults.ts +182 -64
- package/src/routes/config/main.ts +109 -73
- package/src/routes/config/models.ts +304 -137
- package/src/routes/config/providers.ts +462 -166
- package/src/routes/config/utils.ts +2 -2
- package/src/routes/doctor.ts +395 -161
- package/src/routes/files.ts +650 -260
- package/src/routes/git/branch.ts +143 -52
- package/src/routes/git/commit.ts +347 -141
- package/src/routes/git/diff.ts +239 -116
- package/src/routes/git/init.ts +103 -23
- package/src/routes/git/pull.ts +167 -65
- package/src/routes/git/push.ts +222 -117
- package/src/routes/git/remote.ts +401 -100
- package/src/routes/git/staging.ts +502 -141
- package/src/routes/git/status.ts +171 -78
- package/src/routes/mcp.ts +1129 -404
- package/src/routes/openapi.ts +27 -4
- package/src/routes/ottorouter.ts +1221 -389
- package/src/routes/provider-usage.ts +153 -36
- package/src/routes/research.ts +817 -370
- package/src/routes/root.ts +50 -6
- package/src/routes/session-approval.ts +228 -54
- package/src/routes/session-files.ts +265 -134
- package/src/routes/session-messages.ts +330 -150
- package/src/routes/session-stream.ts +83 -2
- package/src/routes/sessions.ts +1830 -780
- package/src/routes/skills.ts +849 -161
- package/src/routes/terminals.ts +469 -103
- package/src/routes/tunnel.ts +394 -118
- package/src/runtime/ask/service.ts +1 -0
- package/src/runtime/message/compaction-limits.ts +3 -3
- package/src/runtime/provider/reasoning.ts +2 -1
- package/src/runtime/session/db-operations.ts +4 -3
- package/src/runtime/utils/token.ts +7 -2
- package/src/tools/adapter.ts +21 -0
- package/src/openapi/paths/ask.ts +0 -81
- package/src/openapi/paths/auth.ts +0 -687
- package/src/openapi/paths/branch.ts +0 -102
- package/src/openapi/paths/config.ts +0 -485
- package/src/openapi/paths/doctor.ts +0 -165
- package/src/openapi/paths/files.ts +0 -236
- package/src/openapi/paths/git.ts +0 -690
- package/src/openapi/paths/mcp.ts +0 -339
- package/src/openapi/paths/messages.ts +0 -103
- package/src/openapi/paths/ottorouter.ts +0 -594
- package/src/openapi/paths/provider-usage.ts +0 -59
- package/src/openapi/paths/research.ts +0 -227
- package/src/openapi/paths/session-approval.ts +0 -93
- package/src/openapi/paths/session-extras.ts +0 -336
- package/src/openapi/paths/session-files.ts +0 -91
- package/src/openapi/paths/sessions.ts +0 -210
- package/src/openapi/paths/skills.ts +0 -377
- package/src/openapi/paths/stream.ts +0 -26
- package/src/openapi/paths/terminals.ts +0 -226
- package/src/openapi/paths/tunnel.ts +0 -163
- package/src/openapi/spec.ts +0 -73
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
getDefault,
|
|
25
25
|
getAuthTypeForProvider,
|
|
26
26
|
} from './utils.ts';
|
|
27
|
+
import { openApiRoute } from '../../openapi/route.ts';
|
|
27
28
|
|
|
28
29
|
const COPILOT_MODELS_URL = 'https://api.githubcopilot.com/models';
|
|
29
30
|
const REMOTE_CATALOG_REFRESH_TTL_MS = 5 * 60 * 1000;
|
|
@@ -78,7 +79,7 @@ function getRemoteCatalogUrl(): string {
|
|
|
78
79
|
|
|
79
80
|
function getModelCatalogProviders(
|
|
80
81
|
cachedCatalog: Awaited<ReturnType<typeof readCachedModelCatalog>>,
|
|
81
|
-
) {
|
|
82
|
+
): Record<string, { models?: ModelInfo[]; label?: string }> {
|
|
82
83
|
return cachedCatalog?.providers ?? (USE_BUILTIN_MODEL_CATALOG ? catalog : {});
|
|
83
84
|
}
|
|
84
85
|
|
|
@@ -278,154 +279,320 @@ function getUiProviderLabel(
|
|
|
278
279
|
}
|
|
279
280
|
|
|
280
281
|
export function registerModelsRoutes(app: Hono) {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
282
|
+
openApiRoute(
|
|
283
|
+
app,
|
|
284
|
+
{
|
|
285
|
+
method: 'get',
|
|
286
|
+
path: '/v1/config/providers/{provider}/models',
|
|
287
|
+
tags: ['config'],
|
|
288
|
+
operationId: 'getProviderModels',
|
|
289
|
+
summary: 'Get available models for a provider',
|
|
290
|
+
parameters: [
|
|
291
|
+
{
|
|
292
|
+
in: 'query',
|
|
293
|
+
name: 'project',
|
|
294
|
+
required: false,
|
|
295
|
+
schema: {
|
|
296
|
+
type: 'string',
|
|
297
|
+
},
|
|
298
|
+
description:
|
|
299
|
+
'Project root override (defaults to current working directory).',
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
in: 'path',
|
|
303
|
+
name: 'provider',
|
|
304
|
+
required: true,
|
|
305
|
+
schema: {
|
|
306
|
+
$ref: '#/components/schemas/Provider',
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
],
|
|
310
|
+
responses: {
|
|
311
|
+
'200': {
|
|
312
|
+
description: 'OK',
|
|
313
|
+
content: {
|
|
314
|
+
'application/json': {
|
|
315
|
+
schema: {
|
|
316
|
+
type: 'object',
|
|
317
|
+
properties: {
|
|
318
|
+
models: {
|
|
319
|
+
type: 'array',
|
|
320
|
+
items: {
|
|
321
|
+
$ref: '#/components/schemas/Model',
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
default: {
|
|
325
|
+
type: 'string',
|
|
326
|
+
nullable: true,
|
|
327
|
+
},
|
|
328
|
+
allowAnyModel: {
|
|
329
|
+
type: 'boolean',
|
|
330
|
+
},
|
|
331
|
+
label: {
|
|
332
|
+
type: 'string',
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
required: ['models', 'allowAnyModel', 'label'],
|
|
336
|
+
},
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
'403': {
|
|
341
|
+
description: 'Provider not authorized',
|
|
342
|
+
content: {
|
|
343
|
+
'application/json': {
|
|
344
|
+
schema: {
|
|
345
|
+
type: 'object',
|
|
346
|
+
properties: {
|
|
347
|
+
error: {
|
|
348
|
+
type: 'string',
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
required: ['error'],
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
'404': {
|
|
357
|
+
description: 'Provider not found',
|
|
358
|
+
content: {
|
|
359
|
+
'application/json': {
|
|
360
|
+
schema: {
|
|
361
|
+
type: 'object',
|
|
362
|
+
properties: {
|
|
363
|
+
error: {
|
|
364
|
+
type: 'string',
|
|
365
|
+
},
|
|
366
|
+
},
|
|
367
|
+
required: ['error'],
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
},
|
|
373
|
+
},
|
|
374
|
+
async (c) => {
|
|
375
|
+
try {
|
|
376
|
+
const embeddedConfig = (
|
|
377
|
+
c as unknown as {
|
|
378
|
+
get: (key: 'embeddedConfig') => EmbeddedAppConfig | undefined;
|
|
379
|
+
}
|
|
380
|
+
).get('embeddedConfig');
|
|
381
|
+
const provider = c.req.param('provider') as ProviderId;
|
|
306
382
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
provider,
|
|
317
|
-
projectRoot,
|
|
318
|
-
);
|
|
319
|
-
if (
|
|
320
|
-
providerDefinition &&
|
|
321
|
-
shouldLazyLoadProviderModels(providerDefinition)
|
|
322
|
-
) {
|
|
323
|
-
void refreshProviderModelsInBackground({
|
|
383
|
+
const projectRoot = c.req.query('project') || process.cwd();
|
|
384
|
+
const cfg = await loadConfig(projectRoot);
|
|
385
|
+
const cachedCatalog = await readCachedModelCatalog();
|
|
386
|
+
const modelCatalogProviders = getModelCatalogProviders(cachedCatalog);
|
|
387
|
+
const providerCatalog = modelCatalogProviders[provider];
|
|
388
|
+
|
|
389
|
+
const authorized = await isProviderAuthorizedHybrid(
|
|
390
|
+
embeddedConfig,
|
|
391
|
+
cfg,
|
|
324
392
|
provider,
|
|
325
|
-
|
|
326
|
-
projectRoot,
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
const filteredModels = getProviderModelsForUi({
|
|
330
|
-
catalogModels: providerCatalog?.models,
|
|
331
|
-
provider,
|
|
332
|
-
authType,
|
|
333
|
-
});
|
|
334
|
-
const copilotAllowedModels =
|
|
335
|
-
provider === 'copilot'
|
|
336
|
-
? await getAuthorizedCopilotModels(projectRoot)
|
|
337
|
-
: null;
|
|
338
|
-
|
|
339
|
-
const availableModels = filterCopilotAvailability(
|
|
340
|
-
provider,
|
|
341
|
-
filteredModels,
|
|
342
|
-
copilotAllowedModels,
|
|
343
|
-
);
|
|
344
|
-
|
|
345
|
-
return c.json({
|
|
346
|
-
models: availableModels.map(toUiModel),
|
|
347
|
-
default: getDefault(
|
|
348
|
-
embeddedConfig?.model,
|
|
349
|
-
embeddedConfig?.defaults?.model,
|
|
350
|
-
cfg.defaults.model,
|
|
351
|
-
),
|
|
352
|
-
allowAnyModel: providerDefinition
|
|
353
|
-
? providerAllowsAnyModel(cfg, provider)
|
|
354
|
-
: undefined,
|
|
355
|
-
label: providerDefinition
|
|
356
|
-
? getUiProviderLabel(providerDefinition)
|
|
357
|
-
: (providerCatalog?.label ?? provider),
|
|
358
|
-
});
|
|
359
|
-
} catch (error) {
|
|
360
|
-
logger.error('Failed to get provider models', error);
|
|
361
|
-
const errorResponse = serializeError(error);
|
|
362
|
-
return c.json(errorResponse, errorResponse.error.status || 500);
|
|
363
|
-
}
|
|
364
|
-
});
|
|
393
|
+
);
|
|
365
394
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
c as unknown as {
|
|
370
|
-
get: (key: 'embeddedConfig') => EmbeddedAppConfig | undefined;
|
|
395
|
+
if (!authorized) {
|
|
396
|
+
logger.warn('Provider not authorized', { provider });
|
|
397
|
+
return c.json({ error: 'Provider not authorized' }, 403);
|
|
371
398
|
}
|
|
372
|
-
).get('embeddedConfig');
|
|
373
|
-
|
|
374
|
-
const projectRoot = c.req.query('project') || process.cwd();
|
|
375
|
-
const cfg = await loadConfig(projectRoot);
|
|
376
|
-
|
|
377
|
-
const authorizedProviders = await getAuthorizedProviders(
|
|
378
|
-
embeddedConfig,
|
|
379
|
-
cfg,
|
|
380
|
-
);
|
|
381
399
|
|
|
382
|
-
const cachedCatalog = await readCachedModelCatalog();
|
|
383
|
-
const modelCatalogProviders = getModelCatalogProviders(cachedCatalog);
|
|
384
|
-
void refreshRemoteCatalogInBackground();
|
|
385
|
-
|
|
386
|
-
const modelsMap: Record<string, UiProviderModels> = {};
|
|
387
|
-
|
|
388
|
-
for (const provider of authorizedProviders) {
|
|
389
|
-
const providerCatalog = modelCatalogProviders[provider];
|
|
390
400
|
const providerDefinition = getProviderDefinition(cfg, provider);
|
|
391
|
-
if (providerCatalog) {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
401
|
+
if (!providerDefinition && !providerCatalog) {
|
|
402
|
+
logger.warn('Provider not found in catalog', { provider });
|
|
403
|
+
return c.json({ error: 'Provider not found' }, 404);
|
|
404
|
+
}
|
|
405
|
+
void refreshRemoteCatalogInBackground();
|
|
406
|
+
|
|
407
|
+
const authType = await getAuthTypeForProvider(
|
|
408
|
+
embeddedConfig,
|
|
409
|
+
provider,
|
|
410
|
+
projectRoot,
|
|
411
|
+
);
|
|
412
|
+
if (
|
|
413
|
+
providerDefinition &&
|
|
414
|
+
shouldLazyLoadProviderModels(providerDefinition)
|
|
415
|
+
) {
|
|
416
|
+
void refreshProviderModelsInBackground({
|
|
397
417
|
provider,
|
|
418
|
+
providerDefinition,
|
|
398
419
|
projectRoot,
|
|
399
|
-
);
|
|
400
|
-
|
|
401
|
-
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
const filteredModels = getProviderModelsForUi({
|
|
423
|
+
catalogModels: providerCatalog?.models,
|
|
424
|
+
provider,
|
|
425
|
+
authType,
|
|
426
|
+
});
|
|
427
|
+
const copilotAllowedModels =
|
|
428
|
+
provider === 'copilot'
|
|
429
|
+
? await getAuthorizedCopilotModels(projectRoot)
|
|
430
|
+
: null;
|
|
431
|
+
|
|
432
|
+
const availableModels = filterCopilotAvailability(
|
|
433
|
+
provider,
|
|
434
|
+
filteredModels,
|
|
435
|
+
copilotAllowedModels,
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
return c.json({
|
|
439
|
+
models: availableModels.map(toUiModel),
|
|
440
|
+
default: getDefault(
|
|
441
|
+
embeddedConfig?.model,
|
|
442
|
+
embeddedConfig?.defaults?.model,
|
|
443
|
+
cfg.defaults.model,
|
|
444
|
+
),
|
|
445
|
+
allowAnyModel: providerDefinition
|
|
446
|
+
? providerAllowsAnyModel(cfg, provider)
|
|
447
|
+
: undefined,
|
|
448
|
+
label: providerDefinition
|
|
449
|
+
? getUiProviderLabel(providerDefinition)
|
|
450
|
+
: (providerCatalog?.label ?? provider),
|
|
451
|
+
});
|
|
452
|
+
} catch (error) {
|
|
453
|
+
logger.error('Failed to get provider models', error);
|
|
454
|
+
const errorResponse = serializeError(error);
|
|
455
|
+
return c.json(errorResponse, errorResponse.error.status || 500);
|
|
456
|
+
}
|
|
457
|
+
},
|
|
458
|
+
);
|
|
459
|
+
|
|
460
|
+
openApiRoute(
|
|
461
|
+
app,
|
|
462
|
+
{
|
|
463
|
+
method: 'get',
|
|
464
|
+
path: '/v1/config/models',
|
|
465
|
+
tags: ['config'],
|
|
466
|
+
operationId: 'getAllModels',
|
|
467
|
+
summary: 'Get all models across authorized providers',
|
|
468
|
+
parameters: [
|
|
469
|
+
{
|
|
470
|
+
in: 'query',
|
|
471
|
+
name: 'project',
|
|
472
|
+
required: false,
|
|
473
|
+
schema: {
|
|
474
|
+
type: 'string',
|
|
475
|
+
},
|
|
476
|
+
description:
|
|
477
|
+
'Project root override (defaults to current working directory).',
|
|
478
|
+
},
|
|
479
|
+
],
|
|
480
|
+
responses: {
|
|
481
|
+
'200': {
|
|
482
|
+
description: 'OK',
|
|
483
|
+
content: {
|
|
484
|
+
'application/json': {
|
|
485
|
+
schema: {
|
|
486
|
+
type: 'object',
|
|
487
|
+
additionalProperties: {
|
|
488
|
+
type: 'object',
|
|
489
|
+
properties: {
|
|
490
|
+
label: {
|
|
491
|
+
type: 'string',
|
|
492
|
+
},
|
|
493
|
+
authType: {
|
|
494
|
+
type: 'string',
|
|
495
|
+
},
|
|
496
|
+
models: {
|
|
497
|
+
type: 'array',
|
|
498
|
+
items: {
|
|
499
|
+
type: 'object',
|
|
500
|
+
properties: {
|
|
501
|
+
id: {
|
|
502
|
+
type: 'string',
|
|
503
|
+
},
|
|
504
|
+
label: {
|
|
505
|
+
type: 'string',
|
|
506
|
+
},
|
|
507
|
+
toolCall: {
|
|
508
|
+
type: 'boolean',
|
|
509
|
+
},
|
|
510
|
+
reasoningText: {
|
|
511
|
+
type: 'boolean',
|
|
512
|
+
},
|
|
513
|
+
vision: {
|
|
514
|
+
type: 'boolean',
|
|
515
|
+
},
|
|
516
|
+
attachment: {
|
|
517
|
+
type: 'boolean',
|
|
518
|
+
},
|
|
519
|
+
},
|
|
520
|
+
required: ['id', 'label'],
|
|
521
|
+
},
|
|
522
|
+
},
|
|
523
|
+
},
|
|
524
|
+
required: ['label', 'models'],
|
|
525
|
+
},
|
|
526
|
+
},
|
|
527
|
+
},
|
|
528
|
+
},
|
|
529
|
+
},
|
|
530
|
+
},
|
|
531
|
+
},
|
|
532
|
+
async (c) => {
|
|
533
|
+
try {
|
|
534
|
+
const embeddedConfig = (
|
|
535
|
+
c as unknown as {
|
|
536
|
+
get: (key: 'embeddedConfig') => EmbeddedAppConfig | undefined;
|
|
537
|
+
}
|
|
538
|
+
).get('embeddedConfig');
|
|
539
|
+
|
|
540
|
+
const projectRoot = c.req.query('project') || process.cwd();
|
|
541
|
+
const cfg = await loadConfig(projectRoot);
|
|
542
|
+
|
|
543
|
+
const authorizedProviders = await getAuthorizedProviders(
|
|
544
|
+
embeddedConfig,
|
|
545
|
+
cfg,
|
|
546
|
+
);
|
|
547
|
+
|
|
548
|
+
const cachedCatalog = await readCachedModelCatalog();
|
|
549
|
+
const modelCatalogProviders = getModelCatalogProviders(cachedCatalog);
|
|
550
|
+
void refreshRemoteCatalogInBackground();
|
|
551
|
+
|
|
552
|
+
const modelsMap: Record<string, UiProviderModels> = {};
|
|
553
|
+
|
|
554
|
+
for (const provider of authorizedProviders) {
|
|
555
|
+
const providerCatalog = modelCatalogProviders[provider];
|
|
556
|
+
const providerDefinition = getProviderDefinition(cfg, provider);
|
|
557
|
+
if (providerCatalog) {
|
|
558
|
+
const dynamicModels =
|
|
559
|
+
providerDefinition &&
|
|
560
|
+
shouldLazyLoadProviderModels(providerDefinition);
|
|
561
|
+
const authType = await getAuthTypeForProvider(
|
|
562
|
+
embeddedConfig,
|
|
402
563
|
provider,
|
|
403
|
-
providerDefinition,
|
|
404
564
|
projectRoot,
|
|
565
|
+
);
|
|
566
|
+
if (dynamicModels && providerDefinition) {
|
|
567
|
+
void refreshProviderModelsInBackground({
|
|
568
|
+
provider,
|
|
569
|
+
providerDefinition,
|
|
570
|
+
projectRoot,
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
const filteredModels = getProviderModelsForUi({
|
|
574
|
+
catalogModels: providerCatalog?.models,
|
|
575
|
+
provider,
|
|
576
|
+
authType,
|
|
405
577
|
});
|
|
578
|
+
modelsMap[provider] = {
|
|
579
|
+
label: providerDefinition
|
|
580
|
+
? getUiProviderLabel(providerDefinition)
|
|
581
|
+
: (providerCatalog.label ?? provider),
|
|
582
|
+
authType,
|
|
583
|
+
allowAnyModel: providerDefinition?.allowAnyModel,
|
|
584
|
+
dynamicModels,
|
|
585
|
+
models: filteredModels.map(toUiModel),
|
|
586
|
+
};
|
|
406
587
|
}
|
|
407
|
-
const filteredModels = getProviderModelsForUi({
|
|
408
|
-
catalogModels: providerCatalog?.models,
|
|
409
|
-
provider,
|
|
410
|
-
authType,
|
|
411
|
-
});
|
|
412
|
-
modelsMap[provider] = {
|
|
413
|
-
label: providerDefinition
|
|
414
|
-
? getUiProviderLabel(providerDefinition)
|
|
415
|
-
: (providerCatalog.label ?? provider),
|
|
416
|
-
authType,
|
|
417
|
-
allowAnyModel: providerDefinition?.allowAnyModel,
|
|
418
|
-
dynamicModels,
|
|
419
|
-
models: filteredModels.map(toUiModel),
|
|
420
|
-
};
|
|
421
588
|
}
|
|
422
|
-
}
|
|
423
589
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
590
|
+
return c.json(modelsMap);
|
|
591
|
+
} catch (error) {
|
|
592
|
+
logger.error('Failed to get all models', error);
|
|
593
|
+
const errorResponse = serializeError(error);
|
|
594
|
+
return c.json(errorResponse, errorResponse.error.status || 500);
|
|
595
|
+
}
|
|
596
|
+
},
|
|
597
|
+
);
|
|
431
598
|
}
|