Skip to content

Commit e134928

Browse files
committed
fix(config): default new providers to numeric names
1 parent a0255a2 commit e134928

10 files changed

Lines changed: 180 additions & 5 deletions
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import assert from 'assert';
2+
3+
import { createProvidersMethods } from '../../web-ui/modules/app.methods.providers.mjs';
4+
import { createClaudeConfigMethods } from '../../web-ui/modules/app.methods.claude-config.mjs';
5+
import { createStartupClaudeMethods } from '../../web-ui/modules/app.methods.startup-claude.mjs';
6+
import {
7+
nextClaudeConfigName,
8+
nextCodexProviderName,
9+
nextNumericProviderName
10+
} from '../../web-ui/modules/provider-default-names.mjs';
11+
12+
test('nextNumericProviderName returns the first free positive numeric name', () => {
13+
assert.strictEqual(nextNumericProviderName(['1', '3', 'foo', '02', 'local']), '4');
14+
assert.strictEqual(nextNumericProviderName([{ name: '1' }, { name: '2' }, { name: 'custom' }]), '3');
15+
assert.strictEqual(nextNumericProviderName(['foo', 'local']), '1');
16+
});
17+
18+
test('Codex add-provider modal defaults to an auto-increment numeric provider name', () => {
19+
const methods = createProvidersMethods({ api: async () => ({ success: true }) });
20+
const context = {
21+
providersList: [
22+
{ name: 'local' },
23+
{ name: '1' },
24+
{ name: '2' },
25+
{ name: 'custom' }
26+
],
27+
newProvider: { name: 'stale', url: 'https://old.example.test', key: 'sk-old', model: 'old' },
28+
showAddProviderKey: true,
29+
showAddModal: false
30+
};
31+
32+
methods.openAddProviderModal.call(context);
33+
34+
assert.strictEqual(context.showAddModal, true);
35+
assert.strictEqual(context.showAddProviderKey, false);
36+
assert.deepStrictEqual(context.newProvider, {
37+
name: '3',
38+
url: '',
39+
key: '',
40+
model: '',
41+
useTransform: false
42+
});
43+
44+
context.providersList.push({ name: '3' });
45+
methods.closeAddModal.call(context);
46+
assert.strictEqual(context.showAddModal, false);
47+
assert.strictEqual(context.newProvider.name, '4');
48+
});
49+
50+
test('Claude add-config modal defaults to an auto-increment numeric config name', () => {
51+
const startupMethods = createStartupClaudeMethods({ api: async () => ({}) });
52+
const claudeMethods = createClaudeConfigMethods({ api: async () => ({}) });
53+
const context = {
54+
claudeConfigs: {
55+
'智谱GLM': {},
56+
'1': {},
57+
'3': {},
58+
'custom': {}
59+
},
60+
newClaudeConfig: { name: 'stale', apiKey: 'sk-old', baseUrl: 'https://old.example.test', model: 'old' },
61+
showAddClaudeConfigKey: true,
62+
showClaudeConfigModal: false
63+
};
64+
65+
startupMethods.openClaudeConfigModal.call(context);
66+
67+
assert.strictEqual(context.showClaudeConfigModal, true);
68+
assert.strictEqual(context.showAddClaudeConfigKey, false);
69+
assert.deepStrictEqual(context.newClaudeConfig, {
70+
name: '2',
71+
apiKey: '',
72+
externalCredentialType: '',
73+
baseUrl: '',
74+
model: '',
75+
targetApi: 'responses'
76+
});
77+
78+
context.claudeConfigs['2'] = {};
79+
claudeMethods.closeClaudeConfigModal.call(context);
80+
assert.strictEqual(context.showClaudeConfigModal, false);
81+
assert.strictEqual(context.newClaudeConfig.name, '4');
82+
});
83+
84+
test('clone provider/config modals keep manual naming instead of auto-numbering', () => {
85+
const codexMethods = createProvidersMethods({ api: async () => ({ success: true }) });
86+
const claudeMethods = createClaudeConfigMethods({ api: async () => ({}) });
87+
88+
const codexContext = {
89+
newProvider: {},
90+
showAddProviderKey: true,
91+
showAddModal: false
92+
};
93+
codexMethods.openCloneProviderModal.call(codexContext, {
94+
name: '1',
95+
url: 'https://codex.example.test/v1',
96+
upstreamUrl: '',
97+
codexmate_bridge: ''
98+
});
99+
assert.strictEqual(codexContext.showAddModal, true);
100+
assert.strictEqual(codexContext.newProvider.name, '');
101+
assert.strictEqual(codexContext.newProvider.url, 'https://codex.example.test/v1');
102+
103+
const claudeContext = {
104+
claudeConfigs: { '1': {}, '2': {} },
105+
newClaudeConfig: {},
106+
showAddClaudeConfigKey: true,
107+
showClaudeConfigModal: false
108+
};
109+
claudeMethods.openCloneClaudeConfigModal.call(claudeContext, '1', {
110+
apiKey: 'sk-source',
111+
baseUrl: 'https://claude.example.test',
112+
model: 'claude-source',
113+
targetApi: 'responses'
114+
});
115+
assert.strictEqual(claudeContext.showClaudeConfigModal, true);
116+
assert.strictEqual(claudeContext.newClaudeConfig.name, '');
117+
assert.strictEqual(claudeContext.newClaudeConfig.baseUrl, 'https://claude.example.test');
118+
});
119+
120+
test('provider default-name helpers read Codex provider lists and Claude config maps', () => {
121+
assert.strictEqual(nextCodexProviderName([{ name: '1' }, { name: '2' }]), '3');
122+
assert.strictEqual(nextClaudeConfigName({ '1': {}, '2': {}, named: {} }), '3');
123+
});

tests/unit/providers-validation.test.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ test('addProvider normalizes trimmed values and submits sanitized payload', asyn
9494
}
9595
}]);
9696
assert.strictEqual(context.showAddModal, false);
97-
assert.deepStrictEqual(context.newProvider, { name: '', url: '', key: '', model: '', useTransform: false });
97+
assert.deepStrictEqual(context.newProvider, { name: '1', url: '', key: '', model: '', useTransform: false });
9898
// c3c9ee5:增删改不再触发 loadAll,改为本地 providersList 增量更新。
9999
assert.deepStrictEqual(loadAllCalls, []);
100100
assert.ok(

tests/unit/run.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ await import(pathToFileURL(path.join(__dirname, 'provider-share-command.test.mjs
5555
await import(pathToFileURL(path.join(__dirname, 'web-ui-preferences.test.mjs')));
5656
await import(pathToFileURL(path.join(__dirname, 'provider-cache-records.test.mjs')));
5757
await import(pathToFileURL(path.join(__dirname, 'providers-validation.test.mjs')));
58+
await import(pathToFileURL(path.join(__dirname, 'provider-default-names.test.mjs')));
5859
await import(pathToFileURL(path.join(__dirname, 'provider-switch-regression.test.mjs')));
5960
await import(pathToFileURL(path.join(__dirname, 'codex-proxy-options.test.mjs')));
6061
await import(pathToFileURL(path.join(__dirname, 'builtin-proxy-responses-shim.test.mjs')));

tests/unit/web-ui-behavior-parity.test.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,7 @@ test('captured bundled app skeleton only exposes expected data key drift versus
576576
'findProviderByName',
577577
'getProviderValidation',
578578
'getShareCommandPrefixInvocation',
579+
'openAddProviderModal',
579580
'invalidateSessionsUsageData',
580581
'isReservedProviderCreationName',
581582
'isSessionLoadNativeDialogEnabled',

web-ui/modules/app.methods.claude-config.mjs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { nextClaudeConfigName } from './provider-default-names.mjs';
2+
13
function normalizeClaudeText(value) {
24
return typeof value === 'string' ? value.trim() : '';
35
}
@@ -378,7 +380,7 @@ export function createClaudeConfigMethods(options = {}) {
378380
this.showClaudeConfigModal = false;
379381
this.showAddClaudeConfigKey = false;
380382
this.newClaudeConfig = {
381-
name: '',
383+
name: nextClaudeConfigName(this.claudeConfigs),
382384
apiKey: '',
383385
externalCredentialType: '',
384386
baseUrl: '',

web-ui/modules/app.methods.providers.mjs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { nextCodexProviderName } from './provider-default-names.mjs';
2+
13
const PROVIDER_NAME_PATTERN = /^[a-zA-Z0-9._-]+$/;
24
const RESERVED_PROXY_PROVIDER_NAME = 'codexmate-proxy';
35
const RESERVED_LOCAL_PROVIDER_NAME = 'local';
@@ -327,6 +329,18 @@ export function createProvidersMethods(options = {}) {
327329
this.showAddModal = true;
328330
},
329331

332+
openAddProviderModal() {
333+
this.newProvider = {
334+
name: nextCodexProviderName(this.providersList),
335+
url: '',
336+
key: '',
337+
model: '',
338+
useTransform: false
339+
};
340+
this.showAddProviderKey = false;
341+
this.showAddModal = true;
342+
},
343+
330344
async openEditModal(provider) {
331345
const requestId = Symbol('openEditModal');
332346
this._openEditModalRequestId = requestId;
@@ -536,7 +550,7 @@ export function createProvidersMethods(options = {}) {
536550
closeAddModal() {
537551
this.showAddModal = false;
538552
this.showAddProviderKey = false;
539-
this.newProvider = { name: '', url: '', key: '', model: '', useTransform: false };
553+
this.newProvider = { name: nextCodexProviderName(this.providersList), url: '', key: '', model: '', useTransform: false };
540554
},
541555

542556
toggleAddProviderKey() {

web-ui/modules/app.methods.startup-claude.mjs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
normalizeClaudeSettingsEnv,
99
normalizeClaudeValue
1010
} from '../logic.mjs';
11+
import { nextClaudeConfigName } from './provider-default-names.mjs';
1112

1213
export function createStartupClaudeMethods(options = {}) {
1314
const {
@@ -573,6 +574,14 @@ export function createStartupClaudeMethods(options = {}) {
573574
},
574575

575576
openClaudeConfigModal() {
577+
this.newClaudeConfig = {
578+
name: nextClaudeConfigName(this.claudeConfigs),
579+
apiKey: '',
580+
externalCredentialType: '',
581+
baseUrl: '',
582+
model: '',
583+
targetApi: 'responses'
584+
};
576585
this.showAddClaudeConfigKey = false;
577586
this.showClaudeConfigModal = true;
578587
},
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
export function nextNumericProviderName(existingNames) {
2+
const used = new Set();
3+
const list = Array.isArray(existingNames) ? existingNames : [];
4+
for (const item of list) {
5+
const name = typeof item === 'string'
6+
? item.trim()
7+
: (item && typeof item.name === 'string' ? item.name.trim() : '');
8+
if (!/^\d+$/.test(name)) continue;
9+
const value = Number(name);
10+
if (Number.isSafeInteger(value) && value > 0) {
11+
used.add(value);
12+
}
13+
}
14+
let next = 1;
15+
while (used.has(next)) next += 1;
16+
return String(next);
17+
}
18+
19+
export function nextCodexProviderName(providers) {
20+
return nextNumericProviderName(Array.isArray(providers) ? providers : []);
21+
}
22+
23+
export function nextClaudeConfigName(configs) {
24+
return nextNumericProviderName(Object.keys(configs && typeof configs === 'object' ? configs : {}));
25+
}

web-ui/partials/index/panel-config-codex.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747

4848
<div class="tool-config-write-scope" :class="{ locked: !isToolConfigWriteAllowed('codex') }">
4949
<div class="tool-config-write-body">
50-
<button class="btn-add" @click="showAddProviderKey = false; showAddModal = true" v-if="!loading && !initError" :disabled="!isToolConfigWriteAllowed('codex')">
50+
<button class="btn-add" @click="openAddProviderModal" v-if="!loading && !initError" :disabled="!isToolConfigWriteAllowed('codex')">
5151
<svg class="icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2"><path d="M10 4v12M4 10h12"/></svg>
5252
{{ t('config.addProvider') }}
5353
</button>

web-ui/res/web-ui-render.precompiled.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1115,7 +1115,7 @@ return function render(_ctx, _cache) {
11151115
? (_openBlock(), _createElementBlock("button", {
11161116
key: 0,
11171117
class: "btn-add",
1118-
onClick: $event => {_ctx.showAddProviderKey = false; _ctx.showAddModal = true},
1118+
onClick: _ctx.openAddProviderModal,
11191119
disabled: !_ctx.isToolConfigWriteAllowed('codex')
11201120
}, [
11211121
(_openBlock(), _createElementBlock("svg", {

0 commit comments

Comments
 (0)