codexmate 0.1.17 → 0.1.18
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/README.md +1 -0
- package/README.vi.md +1 -0
- package/README.zh.md +1 -0
- package/cli.js +130 -3
- package/package.json +1 -1
- package/web-ui/app.js +5 -10
- package/web-ui/index.html +1 -0
- package/web-ui/modules/app.methods.pi-config.mjs +208 -120
- package/web-ui/modules/i18n/locales/en.mjs +13 -0
- package/web-ui/modules/i18n/locales/ja.mjs +13 -0
- package/web-ui/modules/i18n/locales/vi.mjs +13 -0
- package/web-ui/modules/i18n/locales/zh-tw.mjs +13 -0
- package/web-ui/modules/i18n/locales/zh.mjs +13 -0
- package/web-ui/modules/pi-provider-templates.mjs +26 -0
- package/web-ui/partials/index/modals-pi-provider.html +96 -0
- package/web-ui/partials/index/panel-config-pi.html +0 -122
- package/web-ui/res/web-ui-render.precompiled.js +323 -348
- package/web-ui/styles/settings-panel.css +12 -0
package/README.md
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
[](LICENSE)
|
|
23
23
|
[](https://github.com/SakuraByteCore/codexmate/stargazers)
|
|
24
24
|
[](https://github.com/SakuraByteCore/codexmate/issues)
|
|
25
|
+
[](https://stackmap.shipwithai.xyz/repos/SakuraByteCore/codexmate?utm_source=badge)
|
|
25
26
|
|
|
26
27
|
<br />
|
|
27
28
|
|
package/README.vi.md
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
[](LICENSE)
|
|
23
23
|
[](https://github.com/SakuraByteCore/codexmate/stargazers)
|
|
24
24
|
[](https://github.com/SakuraByteCore/codexmate/issues)
|
|
25
|
+
[](https://stackmap.shipwithai.xyz/repos/SakuraByteCore/codexmate?utm_source=badge)
|
|
25
26
|
|
|
26
27
|
<br />
|
|
27
28
|
|
package/README.zh.md
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
[](LICENSE)
|
|
22
22
|
[](https://github.com/SakuraByteCore/codexmate/stargazers)
|
|
23
23
|
[](https://github.com/SakuraByteCore/codexmate/issues)
|
|
24
|
+
[](https://stackmap.shipwithai.xyz/repos/SakuraByteCore/codexmate?utm_source=badge)
|
|
24
25
|
|
|
25
26
|
<br />
|
|
26
27
|
|
package/cli.js
CHANGED
|
@@ -1287,6 +1287,130 @@ async function fetchPiRemoteModels(params = {}) {
|
|
|
1287
1287
|
}
|
|
1288
1288
|
}
|
|
1289
1289
|
|
|
1290
|
+
const PI_MODELS_DEV_API_URL = 'https://models.dev/api.json';
|
|
1291
|
+
const PI_MODELS_CATALOG_CACHE_TTL_MS = 60 * 60 * 1000;
|
|
1292
|
+
const PI_MODELS_DEV_TIMEOUT_MS = 15000;
|
|
1293
|
+
let g_piModelsDevCatalogCache = null;
|
|
1294
|
+
let g_piModelsDevCatalogCachedAt = 0;
|
|
1295
|
+
|
|
1296
|
+
function fetchPiModelsDevCatalog() {
|
|
1297
|
+
if (g_piModelsDevCatalogCache && Date.now() - g_piModelsDevCatalogCachedAt < PI_MODELS_CATALOG_CACHE_TTL_MS) {
|
|
1298
|
+
return Promise.resolve(g_piModelsDevCatalogCache);
|
|
1299
|
+
}
|
|
1300
|
+
return new Promise((resolve) => {
|
|
1301
|
+
const finish = (payload) => {
|
|
1302
|
+
if (payload && !payload.error) {
|
|
1303
|
+
g_piModelsDevCatalogCache = payload;
|
|
1304
|
+
g_piModelsDevCatalogCachedAt = Date.now();
|
|
1305
|
+
}
|
|
1306
|
+
resolve(payload);
|
|
1307
|
+
};
|
|
1308
|
+
const req = https.get(PI_MODELS_DEV_API_URL, {
|
|
1309
|
+
headers: {
|
|
1310
|
+
'User-Agent': 'codexmate-models',
|
|
1311
|
+
'Accept': 'application/json'
|
|
1312
|
+
},
|
|
1313
|
+
agent: HTTPS_KEEP_ALIVE_AGENT
|
|
1314
|
+
}, (res) => {
|
|
1315
|
+
const status = res.statusCode || 0;
|
|
1316
|
+
if (status >= 400) {
|
|
1317
|
+
res.resume();
|
|
1318
|
+
return finish({ error: `HTTP ${status}` });
|
|
1319
|
+
}
|
|
1320
|
+
let body = '';
|
|
1321
|
+
res.on('data', chunk => {
|
|
1322
|
+
body += chunk;
|
|
1323
|
+
});
|
|
1324
|
+
res.on('end', () => {
|
|
1325
|
+
try {
|
|
1326
|
+
const catalog = JSON.parse(body || '{}');
|
|
1327
|
+
if (!catalog || typeof catalog !== 'object' || Array.isArray(catalog)) {
|
|
1328
|
+
return finish({ error: '响应内容不是有效的目录对象' });
|
|
1329
|
+
}
|
|
1330
|
+
finish(catalog);
|
|
1331
|
+
} catch (e) {
|
|
1332
|
+
finish({ error: e && e.message ? e.message : '解析目录响应失败' });
|
|
1333
|
+
}
|
|
1334
|
+
});
|
|
1335
|
+
});
|
|
1336
|
+
req.setTimeout(PI_MODELS_DEV_TIMEOUT_MS, () => {
|
|
1337
|
+
req.destroy(new Error('请求超时'));
|
|
1338
|
+
});
|
|
1339
|
+
req.on('error', (err) => {
|
|
1340
|
+
finish({ error: err && err.message ? err.message : '请求失败' });
|
|
1341
|
+
});
|
|
1342
|
+
});
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
function normalizePiCatalogUrl(url) {
|
|
1346
|
+
return typeof url === 'string' ? url.trim().replace(/\/+$/, '') : '';
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
function findPiCatalogModelInProvider(provider, modelId) {
|
|
1350
|
+
const models = provider && typeof provider === 'object' ? provider.models : null;
|
|
1351
|
+
if (!models || typeof models !== 'object') return null;
|
|
1352
|
+
const hit = models[modelId];
|
|
1353
|
+
return hit && typeof hit === 'object' ? hit : null;
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
function findPiCatalogModel(catalog, lookup) {
|
|
1357
|
+
if (lookup.providerId && catalog[lookup.providerId]) {
|
|
1358
|
+
return findPiCatalogModelInProvider(catalog[lookup.providerId], lookup.modelId);
|
|
1359
|
+
}
|
|
1360
|
+
const entries = Object.entries(catalog);
|
|
1361
|
+
for (const [key, provider] of entries) {
|
|
1362
|
+
const api = normalizePiCatalogUrl(provider && typeof provider === 'object' ? provider.api : '');
|
|
1363
|
+
if ((lookup.baseUrl && api === lookup.baseUrl) || (lookup.providerId && key === lookup.providerId)) {
|
|
1364
|
+
return findPiCatalogModelInProvider(provider, lookup.modelId);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
for (const [, provider] of entries) {
|
|
1368
|
+
const hit = findPiCatalogModelInProvider(provider, lookup.modelId);
|
|
1369
|
+
if (hit) return hit;
|
|
1370
|
+
}
|
|
1371
|
+
return null;
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
function toPiCatalogModelInfo(raw) {
|
|
1375
|
+
const numericOrNull = (value) => (typeof value === 'number' && Number.isFinite(value) ? value : null);
|
|
1376
|
+
const limit = raw && typeof raw.limit === 'object' && raw.limit !== null ? raw.limit : {};
|
|
1377
|
+
const cost = raw && typeof raw.cost === 'object' && raw.cost !== null ? raw.cost : {};
|
|
1378
|
+
return {
|
|
1379
|
+
name: typeof raw.name === 'string' ? raw.name : '',
|
|
1380
|
+
reasoning: !!raw.reasoning,
|
|
1381
|
+
contextWindow: numericOrNull(limit.context),
|
|
1382
|
+
maxTokens: numericOrNull(limit.output),
|
|
1383
|
+
cost: {
|
|
1384
|
+
input: numericOrNull(cost.input),
|
|
1385
|
+
output: numericOrNull(cost.output),
|
|
1386
|
+
cacheRead: numericOrNull(cost.cache_read),
|
|
1387
|
+
cacheWrite: numericOrNull(cost.cache_write)
|
|
1388
|
+
}
|
|
1389
|
+
};
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
async function fetchPiModelsCatalog(params = {}) {
|
|
1393
|
+
const modelId = typeof params.modelId === 'string' ? params.modelId.trim() : '';
|
|
1394
|
+
if (!modelId) {
|
|
1395
|
+
return { error: '模型 ID 不能为空' };
|
|
1396
|
+
}
|
|
1397
|
+
const lookup = {
|
|
1398
|
+
modelId,
|
|
1399
|
+
providerId: typeof params.providerId === 'string' ? params.providerId.trim() : '',
|
|
1400
|
+
baseUrl: normalizePiCatalogUrl(params.baseUrl)
|
|
1401
|
+
};
|
|
1402
|
+
const catalog = await fetchPiModelsDevCatalog();
|
|
1403
|
+
if (!catalog || typeof catalog !== 'object' || typeof catalog.error === 'string') {
|
|
1404
|
+
const reason = catalog && catalog.error ? catalog.error : '响应结构无效';
|
|
1405
|
+
return { error: `models.dev 目录请求失败:${reason}` };
|
|
1406
|
+
}
|
|
1407
|
+
const hit = findPiCatalogModel(catalog, lookup);
|
|
1408
|
+
if (!hit) {
|
|
1409
|
+
return { error: 'models.dev 目录中未找到该模型' };
|
|
1410
|
+
}
|
|
1411
|
+
return { ok: true, model: toPiCatalogModelInfo(hit) };
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1290
1414
|
function readPiSettings(params = {}) {
|
|
1291
1415
|
try {
|
|
1292
1416
|
const dir = getPiAgentDir();
|
|
@@ -2880,7 +3004,6 @@ function addProviderToConfig(params = {}) {
|
|
|
2880
3004
|
let baseUrlForConfig = normalizedUrl;
|
|
2881
3005
|
let authKeyForConfig = key;
|
|
2882
3006
|
const extraLines = [];
|
|
2883
|
-
const requiresOpenaiAuth = useTransform;
|
|
2884
3007
|
|
|
2885
3008
|
if (useTransform) {
|
|
2886
3009
|
const saveRes = upsertOpenaiBridgeProvider(OPENAI_BRIDGE_SETTINGS_FILE, name, normalizedUrl, key, undefined, { maxRetries: openaiBridgeMaxRetries });
|
|
@@ -2905,7 +3028,7 @@ function addProviderToConfig(params = {}) {
|
|
|
2905
3028
|
`name = "${safeName}"`,
|
|
2906
3029
|
`base_url = "${safeUrl}"`,
|
|
2907
3030
|
`wire_api = "responses"`,
|
|
2908
|
-
`requires_openai_auth =
|
|
3031
|
+
`requires_openai_auth = true`,
|
|
2909
3032
|
`preferred_auth_method = "${safeKey}"`,
|
|
2910
3033
|
`models = [{ id = "${escapeTomlBasicString(model)}", name = "${escapeTomlBasicString(model)}" }]`,
|
|
2911
3034
|
...extraLines,
|
|
@@ -10407,7 +10530,7 @@ ${buildModelProviderTableHeader(providerName)}
|
|
|
10407
10530
|
name = "${safeName}"
|
|
10408
10531
|
base_url = "${safeBaseUrl}"
|
|
10409
10532
|
wire_api = "responses"
|
|
10410
|
-
requires_openai_auth =
|
|
10533
|
+
requires_openai_auth = true
|
|
10411
10534
|
preferred_auth_method = "${safeApiKey}"
|
|
10412
10535
|
request_max_retries = 4
|
|
10413
10536
|
stream_max_retries = 10
|
|
@@ -13364,6 +13487,10 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
|
|
|
13364
13487
|
result = await fetchPiRemoteModels(params || {});
|
|
13365
13488
|
break;
|
|
13366
13489
|
}
|
|
13490
|
+
case 'pi-models-catalog': {
|
|
13491
|
+
result = await fetchPiModelsCatalog(params || {});
|
|
13492
|
+
break;
|
|
13493
|
+
}
|
|
13367
13494
|
case 'install-status':
|
|
13368
13495
|
result = buildInstallStatusReport();
|
|
13369
13496
|
break;
|
package/package.json
CHANGED
package/web-ui/app.js
CHANGED
|
@@ -128,16 +128,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
128
128
|
piProviders: {},
|
|
129
129
|
piProviderIds: [],
|
|
130
130
|
editingPiProvider: null,
|
|
131
|
-
addingPiProviderId: '',
|
|
132
|
-
addingPiProviderName: '',
|
|
133
|
-
addingPiProviderBaseUrl: '',
|
|
134
|
-
addingPiProviderApi: 'openai-completions',
|
|
135
|
-
addingPiProviderApiKey: '',
|
|
136
|
-
addingPiProviderModel: '',
|
|
137
|
-
addingPiRemoteModels: [],
|
|
138
|
-
addingPiRemoteLoading: false,
|
|
139
|
-
addingPiRemoteError: '',
|
|
140
|
-
addingPiRemoteDebounce: null,
|
|
141
131
|
showAddPiProviderModal: false,
|
|
142
132
|
piProviderLoading: false,
|
|
143
133
|
piSaving: false,
|
|
@@ -145,6 +135,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
145
135
|
piRemoteModelsLoading: false,
|
|
146
136
|
piRemoteModelError: '',
|
|
147
137
|
piModelSearch: '',
|
|
138
|
+
piProviderPickerQuery: '',
|
|
139
|
+
piSelectedProviderTemplate: '',
|
|
140
|
+
piRemoteChecked: {},
|
|
141
|
+
piCatalogFillIndex: -1,
|
|
142
|
+
piCatalogFillError: '',
|
|
148
143
|
piActiveProvider: '',
|
|
149
144
|
piActiveModel: '',
|
|
150
145
|
piShowKey: false,
|
package/web-ui/index.html
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
<!-- @include ./partials/index/panel-config-opencode.html -->
|
|
20
20
|
<!-- @include ./partials/index/panel-config-kilocode.html -->
|
|
21
21
|
<!-- @include ./partials/index/panel-config-pi.html -->
|
|
22
|
+
<!-- @include ./partials/index/modals-pi-provider.html -->
|
|
22
23
|
<!-- @include ./partials/index/panel-sessions.html -->
|
|
23
24
|
<!-- @include ./partials/index/panel-usage.html -->
|
|
24
25
|
<!-- @include ./partials/index/panel-docs.html -->
|