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 CHANGED
@@ -22,6 +22,7 @@
22
22
  [![License](https://img.shields.io/npm/l/codexmate?style=flat-square)](LICENSE)
23
23
  [![Stars](https://img.shields.io/github/stars/SakuraByteCore/codexmate?style=flat-square&color=gold)](https://github.com/SakuraByteCore/codexmate/stargazers)
24
24
  [![Issues](https://img.shields.io/github/issues/SakuraByteCore/codexmate?style=flat-square&color=ff69b4)](https://github.com/SakuraByteCore/codexmate/issues)
25
+ [![On StackMap](https://img.shields.io/endpoint?url=https%3A%2F%2Fstackmap.shipwithai.xyz%2Fapi%2Fbadge%2Fcodexmate.json)](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](https://img.shields.io/npm/l/codexmate?style=flat-square)](LICENSE)
23
23
  [![Stars](https://img.shields.io/github/stars/SakuraByteCore/codexmate?style=flat-square&color=gold)](https://github.com/SakuraByteCore/codexmate/stargazers)
24
24
  [![Issues](https://img.shields.io/github/issues/SakuraByteCore/codexmate?style=flat-square&color=ff69b4)](https://github.com/SakuraByteCore/codexmate/issues)
25
+ [![On StackMap](https://img.shields.io/endpoint?url=https%3A%2F%2Fstackmap.shipwithai.xyz%2Fapi%2Fbadge%2Fcodexmate.json)](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](https://img.shields.io/npm/l/codexmate?style=flat-square)](LICENSE)
22
22
  [![Stars](https://img.shields.io/github/stars/SakuraByteCore/codexmate?style=flat-square&color=gold)](https://github.com/SakuraByteCore/codexmate/stargazers)
23
23
  [![Issues](https://img.shields.io/github/issues/SakuraByteCore/codexmate?style=flat-square&color=ff69b4)](https://github.com/SakuraByteCore/codexmate/issues)
24
+ [![On StackMap](https://img.shields.io/endpoint?url=https%3A%2F%2Fstackmap.shipwithai.xyz%2Fapi%2Fbadge%2Fcodexmate.json)](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 = ${requiresOpenaiAuth ? 'true' : 'false'}`,
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 = false
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codexmate",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "description": "Codex/Claude Code/OpenClaw 配置、会话与任务编排 CLI + Web 工具",
5
5
  "main": "cli.js",
6
6
  "bin": {
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 -->