@mindexec/cli 0.2.371 → 0.2.373
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 +1 -1
- package/server.js +448 -5
- package/wwwroot/assets/{AdminDashboardPage-3j_fLtJI.js → AdminDashboardPage-CWvvGIat.js} +1 -1
- package/wwwroot/assets/{AppSidebar-NgcRUfE7.js → AppSidebar-C_TF7TSM.js} +2 -2
- package/wwwroot/assets/{AuthPages-Cnj1SLJh.js → AuthPages-wmFxnvR0.js} +1 -1
- package/wwwroot/assets/{CodePage-Do4K07Ge.js → CodePage-D0CxncU0.js} +1 -1
- package/wwwroot/assets/{CompanyCorePage-iD32axX4.js → CompanyCorePage-C8AAF9MJ.js} +1 -1
- package/wwwroot/assets/{ExecutionModePage-BHn-i9eu.js → ExecutionModePage-DM4G1xkT.js} +1 -1
- package/wwwroot/assets/MindCanvas-CCJC3_e5.css +1 -0
- package/wwwroot/assets/MindCanvas-_oNiY0gX.js +48 -0
- package/wwwroot/assets/{PlanMasterPage-B9XXbERQ.js → PlanMasterPage-axrFePG0.js} +4 -4
- package/wwwroot/assets/{PricingPage-Cgbt5ZmJ.js → PricingPage-BGPf-2kS.js} +1 -1
- package/wwwroot/assets/{ToolPages-DLme_QLb.js → ToolPages-CTVDo24Z.js} +5 -5
- package/wwwroot/assets/{YouTubeSearchPage-BSQIhWnY.js → YouTubeSearchPage-BWA45eoM.js} +2 -2
- package/wwwroot/assets/canvas-runtime-CddXXjp9.js +44 -0
- package/wwwroot/assets/{index-D32iV1LN.js → index-Camg1Hkj.js} +2 -2
- package/wwwroot/assets/{storage-C8yTm9Oa.js → storage-BPqBi6qs.js} +1 -1
- package/wwwroot/assets/{supabaseAuthAdapter-C1dNnpk8.js → supabaseAuthAdapter-bSjZe3In.js} +1 -1
- package/wwwroot/index.html +2 -2
- package/wwwroot/assets/MindCanvas-0T8dFIuQ.css +0 -1
- package/wwwroot/assets/MindCanvas-uwqW1baW.js +0 -48
- package/wwwroot/assets/canvas-runtime-CcSvL_l6.js +0 -44
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -2198,10 +2198,11 @@ const PROTECTED_BRIDGE_ROUTES = [
|
|
|
2198
2198
|
{ method: 'GET', exact: '/api/concept/embed/readiness' },
|
|
2199
2199
|
{ method: 'POST', exact: '/api/concept/embed' },
|
|
2200
2200
|
{ method: 'POST', prefix: '/api/browser/act' },
|
|
2201
|
-
{ method: 'GET', prefix: '/api/company-core/' },
|
|
2201
|
+
{ method: 'GET', prefix: '/api/company-core/' },
|
|
2202
2202
|
{ method: 'POST', prefix: '/api/company-core/' },
|
|
2203
2203
|
{ method: 'PUT', prefix: '/api/company-core/' },
|
|
2204
2204
|
{ method: 'DELETE', prefix: '/api/company-core/' },
|
|
2205
|
+
{ method: 'GET', prefix: '/api/model-catalog' },
|
|
2205
2206
|
{ method: 'GET', prefix: '/api/codex/' },
|
|
2206
2207
|
{ method: 'POST', prefix: '/api/codex/' },
|
|
2207
2208
|
{ method: 'GET', prefix: '/api/shell/' },
|
|
@@ -3231,14 +3232,78 @@ function extractCodexOutputPath(command) {
|
|
|
3231
3232
|
return match[2] || match[3] || match[4] || null;
|
|
3232
3233
|
}
|
|
3233
3234
|
|
|
3234
|
-
const CODEX_CONFIG_PATH = path.join(os.homedir(), '.codex', 'config.toml');
|
|
3235
|
-
const CODEX_MODEL_CATALOG_TTL_MS = 15 * 60 * 1000;
|
|
3236
|
-
|
|
3237
|
-
let
|
|
3235
|
+
const CODEX_CONFIG_PATH = path.join(os.homedir(), '.codex', 'config.toml');
|
|
3236
|
+
const CODEX_MODEL_CATALOG_TTL_MS = 15 * 60 * 1000;
|
|
3237
|
+
const EXTERNAL_MODEL_CATALOG_TTL_MS = 60 * 1000;
|
|
3238
|
+
let cachedCodexConfig = null;
|
|
3239
|
+
let cachedCodexConfigMtimeMs = -1;
|
|
3238
3240
|
let cachedCodexModelCatalog = null;
|
|
3239
3241
|
let cachedCodexModelCatalogExpiresAt = 0;
|
|
3242
|
+
let cachedBridgeModelCatalog = null;
|
|
3243
|
+
let cachedBridgeModelCatalogExpiresAt = 0;
|
|
3240
3244
|
let lastCodexExecRuntime = null;
|
|
3241
3245
|
let codexRuntime = null;
|
|
3246
|
+
|
|
3247
|
+
const FALLBACK_CODEX_TEXT_MODELS = [
|
|
3248
|
+
{ id: 'gpt-5.5', displayName: 'GPT-5.5' },
|
|
3249
|
+
{ id: 'gpt-5.4', displayName: 'GPT-5.4' },
|
|
3250
|
+
{ id: 'gpt-5.4-mini', displayName: 'GPT-5.4 mini' },
|
|
3251
|
+
{ id: 'gpt-5.3-codex-spark', displayName: 'GPT-5.3 Codex Spark' }
|
|
3252
|
+
];
|
|
3253
|
+
|
|
3254
|
+
const FALLBACK_OPENROUTER_TEXT_MODELS = [
|
|
3255
|
+
{ id: 'openrouter/auto', displayName: 'Auto Router', isFallback: true },
|
|
3256
|
+
{ id: 'openai/gpt-chat-latest', displayName: 'GPT Chat Latest', isFallback: true },
|
|
3257
|
+
{ id: 'anthropic/claude-sonnet-4.5', displayName: 'Claude Sonnet 4.5', supportsReasoning: true, isFallback: true },
|
|
3258
|
+
{ id: 'google/gemini-2.5-pro', displayName: 'Gemini 2.5 Pro', supportsReasoning: true, isFallback: true },
|
|
3259
|
+
{ id: 'google/gemini-2.5-flash', displayName: 'Gemini 2.5 Flash', isFallback: true }
|
|
3260
|
+
];
|
|
3261
|
+
|
|
3262
|
+
const FALLBACK_IMAGE_MODELS = [
|
|
3263
|
+
{
|
|
3264
|
+
id: 'gpt-image-2',
|
|
3265
|
+
label: 'GPT Image 2',
|
|
3266
|
+
displayName: 'GPT Image 2',
|
|
3267
|
+
provider: 'openai',
|
|
3268
|
+
mode: 'image',
|
|
3269
|
+
supportedSizes: ['custom'],
|
|
3270
|
+
vendorPricing: { averageUsd: 0.02, source: 'fallback' },
|
|
3271
|
+
isFallback: true
|
|
3272
|
+
},
|
|
3273
|
+
{
|
|
3274
|
+
id: 'Tongyi-MAI/Z-Image-Turbo:free',
|
|
3275
|
+
label: 'Z-Image Turbo',
|
|
3276
|
+
displayName: 'Z-Image Turbo',
|
|
3277
|
+
provider: 'imagerouter',
|
|
3278
|
+
mode: 'image',
|
|
3279
|
+
supportedSizes: ['custom'],
|
|
3280
|
+
vendorPricing: { averageUsd: 0.02, source: 'fallback' },
|
|
3281
|
+
isFallback: true
|
|
3282
|
+
}
|
|
3283
|
+
];
|
|
3284
|
+
|
|
3285
|
+
const FALLBACK_VIDEO_MODELS = [
|
|
3286
|
+
{
|
|
3287
|
+
id: 'google/veo-2',
|
|
3288
|
+
label: 'Google Veo 2',
|
|
3289
|
+
displayName: 'Google Veo 2',
|
|
3290
|
+
provider: 'imagerouter',
|
|
3291
|
+
mode: 'video',
|
|
3292
|
+
supportedSizes: ['1280x720', '720x1280'],
|
|
3293
|
+
vendorPricing: { averageUsd: 0.25, maxUsd: 0.25, source: 'fallback' },
|
|
3294
|
+
isFallback: true
|
|
3295
|
+
},
|
|
3296
|
+
{
|
|
3297
|
+
id: 'runway/gen-3',
|
|
3298
|
+
label: 'Runway Gen-3',
|
|
3299
|
+
displayName: 'Runway Gen-3',
|
|
3300
|
+
provider: 'imagerouter',
|
|
3301
|
+
mode: 'video',
|
|
3302
|
+
supportedSizes: ['1280x720', '720x1280'],
|
|
3303
|
+
vendorPricing: { averageUsd: 0.20, maxUsd: 0.20, source: 'fallback' },
|
|
3304
|
+
isFallback: true
|
|
3305
|
+
}
|
|
3306
|
+
];
|
|
3242
3307
|
|
|
3243
3308
|
function readCodexConfig(options = {}) {
|
|
3244
3309
|
const forceRefresh = Boolean(options.forceRefresh);
|
|
@@ -3409,6 +3474,371 @@ async function getCodexModelCatalog(options = {}) {
|
|
|
3409
3474
|
}
|
|
3410
3475
|
}
|
|
3411
3476
|
|
|
3477
|
+
function getPlainString(value) {
|
|
3478
|
+
return String(value ?? '').trim();
|
|
3479
|
+
}
|
|
3480
|
+
|
|
3481
|
+
function getCatalogString(item, propertyName) {
|
|
3482
|
+
if (!item || typeof item !== 'object') {
|
|
3483
|
+
return '';
|
|
3484
|
+
}
|
|
3485
|
+
|
|
3486
|
+
return getPlainString(item[propertyName]);
|
|
3487
|
+
}
|
|
3488
|
+
|
|
3489
|
+
function getCatalogNumber(item, propertyName) {
|
|
3490
|
+
if (!item || typeof item !== 'object') {
|
|
3491
|
+
return null;
|
|
3492
|
+
}
|
|
3493
|
+
|
|
3494
|
+
const value = Number(item[propertyName]);
|
|
3495
|
+
return Number.isFinite(value) ? value : null;
|
|
3496
|
+
}
|
|
3497
|
+
|
|
3498
|
+
function getCatalogStringArray(item, propertyName) {
|
|
3499
|
+
if (!item || typeof item !== 'object' || !Array.isArray(item[propertyName])) {
|
|
3500
|
+
return [];
|
|
3501
|
+
}
|
|
3502
|
+
|
|
3503
|
+
return [...new Set(item[propertyName]
|
|
3504
|
+
.map(entry => getPlainString(entry))
|
|
3505
|
+
.filter(Boolean))];
|
|
3506
|
+
}
|
|
3507
|
+
|
|
3508
|
+
function getNestedCatalogStringArray(item, objectPropertyName, arrayPropertyName) {
|
|
3509
|
+
if (!item || typeof item !== 'object') {
|
|
3510
|
+
return [];
|
|
3511
|
+
}
|
|
3512
|
+
|
|
3513
|
+
return getCatalogStringArray(item[objectPropertyName], arrayPropertyName);
|
|
3514
|
+
}
|
|
3515
|
+
|
|
3516
|
+
function getNestedCatalogBoolean(item, objectPropertyName, propertyName) {
|
|
3517
|
+
if (!item || typeof item !== 'object') {
|
|
3518
|
+
return false;
|
|
3519
|
+
}
|
|
3520
|
+
|
|
3521
|
+
const parent = item[objectPropertyName];
|
|
3522
|
+
if (!parent || typeof parent !== 'object') {
|
|
3523
|
+
return false;
|
|
3524
|
+
}
|
|
3525
|
+
|
|
3526
|
+
const value = parent[propertyName];
|
|
3527
|
+
return value === true || /^(1|true|yes|on)$/i.test(String(value ?? ''));
|
|
3528
|
+
}
|
|
3529
|
+
|
|
3530
|
+
function getCatalogDecimal(item, propertyName) {
|
|
3531
|
+
const value = getCatalogNumber(item, propertyName);
|
|
3532
|
+
return value === null ? undefined : value;
|
|
3533
|
+
}
|
|
3534
|
+
|
|
3535
|
+
function getOpenRouterPricing(item) {
|
|
3536
|
+
const pricing = item?.pricing;
|
|
3537
|
+
if (!pricing || typeof pricing !== 'object') {
|
|
3538
|
+
return null;
|
|
3539
|
+
}
|
|
3540
|
+
|
|
3541
|
+
const promptUsdPerToken = getCatalogDecimal(pricing, 'prompt');
|
|
3542
|
+
const completionUsdPerToken = getCatalogDecimal(pricing, 'completion');
|
|
3543
|
+
const requestUsd = getCatalogDecimal(pricing, 'request');
|
|
3544
|
+
if (promptUsdPerToken === undefined && completionUsdPerToken === undefined && requestUsd === undefined) {
|
|
3545
|
+
return null;
|
|
3546
|
+
}
|
|
3547
|
+
|
|
3548
|
+
return {
|
|
3549
|
+
promptUsdPerToken,
|
|
3550
|
+
completionUsdPerToken,
|
|
3551
|
+
requestUsd,
|
|
3552
|
+
source: 'openrouter',
|
|
3553
|
+
fetchedAt: new Date().toISOString()
|
|
3554
|
+
};
|
|
3555
|
+
}
|
|
3556
|
+
|
|
3557
|
+
function getImageRouterPricing(item) {
|
|
3558
|
+
const price = item?.price;
|
|
3559
|
+
if (!price || typeof price !== 'object') {
|
|
3560
|
+
return null;
|
|
3561
|
+
}
|
|
3562
|
+
|
|
3563
|
+
const minUsd = getCatalogDecimal(price, 'min');
|
|
3564
|
+
const averageUsd = getCatalogDecimal(price, 'average');
|
|
3565
|
+
const maxUsd = getCatalogDecimal(price, 'max');
|
|
3566
|
+
if (minUsd === undefined && averageUsd === undefined && maxUsd === undefined) {
|
|
3567
|
+
return null;
|
|
3568
|
+
}
|
|
3569
|
+
|
|
3570
|
+
return {
|
|
3571
|
+
minUsd,
|
|
3572
|
+
averageUsd,
|
|
3573
|
+
maxUsd,
|
|
3574
|
+
source: 'imagerouter',
|
|
3575
|
+
fetchedAt: new Date().toISOString()
|
|
3576
|
+
};
|
|
3577
|
+
}
|
|
3578
|
+
|
|
3579
|
+
function buildCatalogDisplayLabel(name, id) {
|
|
3580
|
+
const label = getPlainString(name);
|
|
3581
|
+
const modelId = getPlainString(id);
|
|
3582
|
+
if (!label || label.toLowerCase() === modelId.toLowerCase()) {
|
|
3583
|
+
return modelId;
|
|
3584
|
+
}
|
|
3585
|
+
|
|
3586
|
+
return `${label} (${modelId})`;
|
|
3587
|
+
}
|
|
3588
|
+
|
|
3589
|
+
function containsCatalogString(item, propertyName, expected) {
|
|
3590
|
+
return getCatalogStringArray(item, propertyName)
|
|
3591
|
+
.some(value => value.toLowerCase() === String(expected).toLowerCase());
|
|
3592
|
+
}
|
|
3593
|
+
|
|
3594
|
+
function containsAnyCatalogString(item, propertyName, expectedValues) {
|
|
3595
|
+
const expected = new Set(expectedValues.map(value => String(value).toLowerCase()));
|
|
3596
|
+
return getCatalogStringArray(item, propertyName)
|
|
3597
|
+
.some(value => expected.has(value.toLowerCase()));
|
|
3598
|
+
}
|
|
3599
|
+
|
|
3600
|
+
function hasCatalogMediaModalityInElement(item) {
|
|
3601
|
+
const modality = getCatalogString(item, 'modality').toLowerCase();
|
|
3602
|
+
return containsAnyCatalogString(item, 'input_modalities', ['image', 'video', 'audio'])
|
|
3603
|
+
|| containsAnyCatalogString(item, 'output_modalities', ['image', 'video', 'audio'])
|
|
3604
|
+
|| /image|video|audio/i.test(modality);
|
|
3605
|
+
}
|
|
3606
|
+
|
|
3607
|
+
function supportsOpenRouterTextOnlyOutput(item) {
|
|
3608
|
+
const architecture = item?.architecture && typeof item.architecture === 'object'
|
|
3609
|
+
? item.architecture
|
|
3610
|
+
: null;
|
|
3611
|
+
const supportsText = containsCatalogString(item, 'output_modalities', 'text')
|
|
3612
|
+
|| (architecture && containsCatalogString(architecture, 'output_modalities', 'text'));
|
|
3613
|
+
const hasMedia = hasCatalogMediaModalityInElement(item)
|
|
3614
|
+
|| (architecture && hasCatalogMediaModalityInElement(architecture));
|
|
3615
|
+
return supportsText && !hasMedia;
|
|
3616
|
+
}
|
|
3617
|
+
|
|
3618
|
+
function normalizeOpenRouterModel(item) {
|
|
3619
|
+
const id = getCatalogString(item, 'id');
|
|
3620
|
+
if (!id || !supportsOpenRouterTextOnlyOutput(item)) {
|
|
3621
|
+
return null;
|
|
3622
|
+
}
|
|
3623
|
+
|
|
3624
|
+
const name = getCatalogString(item, 'name');
|
|
3625
|
+
const supportedParameters = getCatalogStringArray(item, 'supported_parameters');
|
|
3626
|
+
return {
|
|
3627
|
+
id,
|
|
3628
|
+
label: buildCatalogDisplayLabel(name, id),
|
|
3629
|
+
displayName: name || id,
|
|
3630
|
+
provider: 'openrouter',
|
|
3631
|
+
mode: 'text',
|
|
3632
|
+
supportsReasoning: supportedParameters.some(value => value.toLowerCase() === 'reasoning'),
|
|
3633
|
+
vendorPricing: getOpenRouterPricing(item),
|
|
3634
|
+
source: 'openrouter'
|
|
3635
|
+
};
|
|
3636
|
+
}
|
|
3637
|
+
|
|
3638
|
+
async function fetchJsonWithTimeout(url, timeoutMs = 10000) {
|
|
3639
|
+
const controller = new AbortController();
|
|
3640
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
3641
|
+
try {
|
|
3642
|
+
const response = await fetch(url, {
|
|
3643
|
+
headers: {
|
|
3644
|
+
Accept: 'application/json',
|
|
3645
|
+
'Cache-Control': 'no-store',
|
|
3646
|
+
Pragma: 'no-cache'
|
|
3647
|
+
},
|
|
3648
|
+
signal: controller.signal
|
|
3649
|
+
});
|
|
3650
|
+
if (!response.ok) {
|
|
3651
|
+
throw new Error(`HTTP ${response.status}`);
|
|
3652
|
+
}
|
|
3653
|
+
return await response.json();
|
|
3654
|
+
} finally {
|
|
3655
|
+
clearTimeout(timeout);
|
|
3656
|
+
}
|
|
3657
|
+
}
|
|
3658
|
+
|
|
3659
|
+
async function getOpenRouterTextModelCatalog() {
|
|
3660
|
+
const payload = await fetchJsonWithTimeout('https://openrouter.ai/api/v1/models', 10000);
|
|
3661
|
+
const items = Array.isArray(payload?.data) ? payload.data : [];
|
|
3662
|
+
const options = items
|
|
3663
|
+
.map(normalizeOpenRouterModel)
|
|
3664
|
+
.filter(Boolean)
|
|
3665
|
+
.sort((a, b) => String(a.label).localeCompare(String(b.label)));
|
|
3666
|
+
return options.length > 0
|
|
3667
|
+
? options
|
|
3668
|
+
: FALLBACK_OPENROUTER_TEXT_MODELS.map(option => ({
|
|
3669
|
+
id: option.id,
|
|
3670
|
+
label: option.displayName,
|
|
3671
|
+
displayName: option.displayName,
|
|
3672
|
+
provider: 'openrouter',
|
|
3673
|
+
mode: 'text',
|
|
3674
|
+
supportsReasoning: option.supportsReasoning === true,
|
|
3675
|
+
isFallback: true,
|
|
3676
|
+
source: 'fallback'
|
|
3677
|
+
}));
|
|
3678
|
+
}
|
|
3679
|
+
|
|
3680
|
+
function normalizeImageRouterModel(item, outputType) {
|
|
3681
|
+
const id = getCatalogString(item, 'id');
|
|
3682
|
+
if (!id) {
|
|
3683
|
+
return null;
|
|
3684
|
+
}
|
|
3685
|
+
|
|
3686
|
+
const displayName = getCatalogString(item, 'name') || id;
|
|
3687
|
+
const supportedSizes = getNestedCatalogStringArray(item, 'inputs', 'size');
|
|
3688
|
+
const supportedSeconds = getNestedCatalogStringArray(item, 'inputs', 'seconds');
|
|
3689
|
+
return {
|
|
3690
|
+
id,
|
|
3691
|
+
label: displayName,
|
|
3692
|
+
displayName,
|
|
3693
|
+
provider: 'imagerouter',
|
|
3694
|
+
mode: outputType,
|
|
3695
|
+
supportedSizes: supportedSizes.length > 0 ? supportedSizes : (outputType === 'image' ? ['custom'] : []),
|
|
3696
|
+
supportedSeconds,
|
|
3697
|
+
capabilities: {
|
|
3698
|
+
supportsTextToImage: outputType === 'image',
|
|
3699
|
+
supportsImageToImage: getNestedCatalogBoolean(item, 'inputs', 'image'),
|
|
3700
|
+
supportsInpaint: getNestedCatalogBoolean(item, 'inputs', 'mask'),
|
|
3701
|
+
supportsSizeSelection: supportedSizes.length > 0,
|
|
3702
|
+
supportedSizes,
|
|
3703
|
+
supportedSeconds
|
|
3704
|
+
},
|
|
3705
|
+
vendorPricing: getImageRouterPricing(item),
|
|
3706
|
+
source: 'imagerouter'
|
|
3707
|
+
};
|
|
3708
|
+
}
|
|
3709
|
+
|
|
3710
|
+
async function getImageRouterModelCatalog(outputType) {
|
|
3711
|
+
const endpoint = new URL('https://api.imagerouter.io/v2/models');
|
|
3712
|
+
endpoint.searchParams.set('outputType', outputType);
|
|
3713
|
+
endpoint.searchParams.set('limit', '200');
|
|
3714
|
+
endpoint.searchParams.set('fields', 'name,price,inputs');
|
|
3715
|
+
|
|
3716
|
+
const payload = await fetchJsonWithTimeout(endpoint.toString(), 10000);
|
|
3717
|
+
const items = Array.isArray(payload)
|
|
3718
|
+
? payload
|
|
3719
|
+
: Array.isArray(payload?.data)
|
|
3720
|
+
? payload.data
|
|
3721
|
+
: [];
|
|
3722
|
+
const options = items
|
|
3723
|
+
.map(item => normalizeImageRouterModel(item, outputType))
|
|
3724
|
+
.filter(Boolean)
|
|
3725
|
+
.sort((a, b) => String(a.label).localeCompare(String(b.label)));
|
|
3726
|
+
|
|
3727
|
+
if (options.length > 0) {
|
|
3728
|
+
return options;
|
|
3729
|
+
}
|
|
3730
|
+
|
|
3731
|
+
return (outputType === 'image' ? FALLBACK_IMAGE_MODELS : FALLBACK_VIDEO_MODELS)
|
|
3732
|
+
.map(option => ({ ...option, source: 'fallback' }));
|
|
3733
|
+
}
|
|
3734
|
+
|
|
3735
|
+
function normalizeCodexCatalogOption(model) {
|
|
3736
|
+
if (!model || typeof model !== 'object') {
|
|
3737
|
+
return null;
|
|
3738
|
+
}
|
|
3739
|
+
|
|
3740
|
+
const id = getCatalogString(model, 'id');
|
|
3741
|
+
if (!id) {
|
|
3742
|
+
return null;
|
|
3743
|
+
}
|
|
3744
|
+
|
|
3745
|
+
const displayName = getCatalogString(model, 'displayName') || id;
|
|
3746
|
+
return {
|
|
3747
|
+
id,
|
|
3748
|
+
label: `OpenAI Codex - ${displayName}`,
|
|
3749
|
+
displayName,
|
|
3750
|
+
description: getCatalogString(model, 'description'),
|
|
3751
|
+
provider: 'codex',
|
|
3752
|
+
mode: 'text',
|
|
3753
|
+
supportsReasoning: true,
|
|
3754
|
+
source: 'codex debug models',
|
|
3755
|
+
priority: getCatalogNumber(model, 'priority') ?? 999,
|
|
3756
|
+
defaultReasoningLevel: getCatalogString(model, 'defaultReasoningLevel'),
|
|
3757
|
+
supportedReasoningLevels: getCatalogStringArray(model, 'supportedReasoningLevels'),
|
|
3758
|
+
supportedInApi: model.supportedInApi === true
|
|
3759
|
+
};
|
|
3760
|
+
}
|
|
3761
|
+
|
|
3762
|
+
async function getBridgeModelCatalog(options = {}) {
|
|
3763
|
+
const now = Date.now();
|
|
3764
|
+
if (!options.forceRefresh && cachedBridgeModelCatalog && cachedBridgeModelCatalogExpiresAt > now) {
|
|
3765
|
+
return cachedBridgeModelCatalog;
|
|
3766
|
+
}
|
|
3767
|
+
|
|
3768
|
+
const errors = [];
|
|
3769
|
+
const codexCatalog = await getCodexModelCatalog({ forceRefresh: options.forceRefresh });
|
|
3770
|
+
if (codexCatalog?.error) {
|
|
3771
|
+
errors.push(`codex: ${codexCatalog.error}`);
|
|
3772
|
+
}
|
|
3773
|
+
|
|
3774
|
+
let codexTextModels = (codexCatalog?.models || [])
|
|
3775
|
+
.map(normalizeCodexCatalogOption)
|
|
3776
|
+
.filter(Boolean)
|
|
3777
|
+
.sort((a, b) => (a.priority ?? 999) - (b.priority ?? 999) || a.id.localeCompare(b.id));
|
|
3778
|
+
if (codexTextModels.length === 0) {
|
|
3779
|
+
codexTextModels = FALLBACK_CODEX_TEXT_MODELS.map((model, index) => ({
|
|
3780
|
+
id: model.id,
|
|
3781
|
+
label: `OpenAI Codex - ${model.displayName}`,
|
|
3782
|
+
displayName: model.displayName,
|
|
3783
|
+
provider: 'codex',
|
|
3784
|
+
mode: 'text',
|
|
3785
|
+
supportsReasoning: true,
|
|
3786
|
+
priority: index,
|
|
3787
|
+
isFallback: true,
|
|
3788
|
+
source: 'fallback'
|
|
3789
|
+
}));
|
|
3790
|
+
}
|
|
3791
|
+
|
|
3792
|
+
const [openRouterResult, imageResult, videoResult] = await Promise.allSettled([
|
|
3793
|
+
getOpenRouterTextModelCatalog(),
|
|
3794
|
+
getImageRouterModelCatalog('image'),
|
|
3795
|
+
getImageRouterModelCatalog('video')
|
|
3796
|
+
]);
|
|
3797
|
+
|
|
3798
|
+
const openRouterTextModels = openRouterResult.status === 'fulfilled'
|
|
3799
|
+
? openRouterResult.value
|
|
3800
|
+
: FALLBACK_OPENROUTER_TEXT_MODELS.map(option => ({
|
|
3801
|
+
id: option.id,
|
|
3802
|
+
label: option.displayName,
|
|
3803
|
+
displayName: option.displayName,
|
|
3804
|
+
provider: 'openrouter',
|
|
3805
|
+
mode: 'text',
|
|
3806
|
+
supportsReasoning: option.supportsReasoning === true,
|
|
3807
|
+
isFallback: true,
|
|
3808
|
+
source: 'fallback'
|
|
3809
|
+
}));
|
|
3810
|
+
if (openRouterResult.status === 'rejected') {
|
|
3811
|
+
errors.push(`openrouter: ${openRouterResult.reason?.message || String(openRouterResult.reason)}`);
|
|
3812
|
+
}
|
|
3813
|
+
|
|
3814
|
+
const imageModels = imageResult.status === 'fulfilled'
|
|
3815
|
+
? imageResult.value
|
|
3816
|
+
: FALLBACK_IMAGE_MODELS.map(option => ({ ...option, source: 'fallback' }));
|
|
3817
|
+
if (imageResult.status === 'rejected') {
|
|
3818
|
+
errors.push(`imagerouter:image: ${imageResult.reason?.message || String(imageResult.reason)}`);
|
|
3819
|
+
}
|
|
3820
|
+
|
|
3821
|
+
const videoModels = videoResult.status === 'fulfilled'
|
|
3822
|
+
? videoResult.value
|
|
3823
|
+
: FALLBACK_VIDEO_MODELS.map(option => ({ ...option, source: 'fallback' }));
|
|
3824
|
+
if (videoResult.status === 'rejected') {
|
|
3825
|
+
errors.push(`imagerouter:video: ${videoResult.reason?.message || String(videoResult.reason)}`);
|
|
3826
|
+
}
|
|
3827
|
+
|
|
3828
|
+
const updatedAt = new Date().toISOString();
|
|
3829
|
+
cachedBridgeModelCatalog = {
|
|
3830
|
+
success: true,
|
|
3831
|
+
textModels: [...codexTextModels, ...openRouterTextModels],
|
|
3832
|
+
imageModels,
|
|
3833
|
+
videoModels,
|
|
3834
|
+
source: 'localbridge-model-catalog',
|
|
3835
|
+
updatedAt,
|
|
3836
|
+
errors
|
|
3837
|
+
};
|
|
3838
|
+
cachedBridgeModelCatalogExpiresAt = now + EXTERNAL_MODEL_CATALOG_TTL_MS;
|
|
3839
|
+
return cachedBridgeModelCatalog;
|
|
3840
|
+
}
|
|
3841
|
+
|
|
3412
3842
|
function getCodexRuntime() {
|
|
3413
3843
|
if (!codexRuntime) {
|
|
3414
3844
|
codexRuntime = createCodexRuntime({
|
|
@@ -13601,6 +14031,19 @@ app.get('/api/codex/capabilities', async (req, res) => {
|
|
|
13601
14031
|
}
|
|
13602
14032
|
});
|
|
13603
14033
|
|
|
14034
|
+
app.get('/api/model-catalog', async (req, res) => {
|
|
14035
|
+
try {
|
|
14036
|
+
const forceRefresh = /^(1|true|yes|on)$/i.test(String(req.query?.refresh || ''));
|
|
14037
|
+
res.json(await getBridgeModelCatalog({ forceRefresh }));
|
|
14038
|
+
} catch (err) {
|
|
14039
|
+
console.error('[ModelCatalog] Error:', err);
|
|
14040
|
+
res.status(500).json({
|
|
14041
|
+
success: false,
|
|
14042
|
+
error: err?.message || String(err)
|
|
14043
|
+
});
|
|
14044
|
+
}
|
|
14045
|
+
});
|
|
14046
|
+
|
|
13604
14047
|
app.post('/api/codex/thread/start', async (req, res) => {
|
|
13605
14048
|
try {
|
|
13606
14049
|
res.json(await getCodexRuntime().startThread(req.body || {}));
|