codexmate 0.0.43 → 0.0.44
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 +2 -0
- package/README.zh.md +2 -0
- package/cli/claude-proxy.js +611 -14
- package/cli/update.js +77 -7
- package/cli.js +188 -21
- package/package.json +1 -1
- package/web-ui/app.js +21 -2
- package/web-ui/logic.claude.mjs +65 -2
- package/web-ui/logic.runtime.mjs +0 -7
- package/web-ui/modules/app.methods.claude-config.mjs +28 -12
- package/web-ui/modules/app.methods.index.mjs +1 -1
- package/web-ui/modules/app.methods.install.mjs +129 -1
- package/web-ui/modules/app.methods.session-actions.mjs +7 -2
- package/web-ui/modules/app.methods.session-timeline.mjs +0 -1
- package/web-ui/modules/app.methods.startup-claude.mjs +26 -3
- package/web-ui/modules/i18n/locales/en.mjs +18 -0
- package/web-ui/modules/i18n/locales/ja.mjs +18 -0
- package/web-ui/modules/i18n/locales/vi.mjs +20 -0
- package/web-ui/modules/i18n/locales/zh.mjs +18 -0
- package/web-ui/partials/index/layout-header.html +24 -0
- package/web-ui/partials/index/modals-basic.html +18 -1
- package/web-ui/partials/index/panel-config-claude.html +4 -1
- package/web-ui/partials/index/panel-config-codex.html +2 -1
- package/web-ui/res/web-ui-render.precompiled.js +114 -21
- package/web-ui/styles/controls-forms.css +5 -5
- package/web-ui/styles/layout-shell.css +145 -0
- package/web-ui/styles/responsive.css +12 -0
|
@@ -23,6 +23,10 @@ function getClaudeConfigValidationForContext(vm, mode = 'add') {
|
|
|
23
23
|
const externalCredentialType = normalizeClaudeText(draft && draft.externalCredentialType);
|
|
24
24
|
const baseUrl = normalizeClaudeBaseUrl(draft && draft.baseUrl);
|
|
25
25
|
const model = normalizeClaudeText(draft && draft.model);
|
|
26
|
+
const targetApiRaw = normalizeClaudeText(draft && draft.targetApi).toLowerCase();
|
|
27
|
+
const targetApi = targetApiRaw === 'chat_completions' || targetApiRaw === 'chat-completions' || targetApiRaw === 'chat/completions'
|
|
28
|
+
? 'chat_completions'
|
|
29
|
+
: (targetApiRaw === 'ollama' ? 'ollama' : 'responses');
|
|
26
30
|
const errors = {
|
|
27
31
|
name: '',
|
|
28
32
|
apiKey: '',
|
|
@@ -36,7 +40,7 @@ function getClaudeConfigValidationForContext(vm, mode = 'add') {
|
|
|
36
40
|
errors.name = vm.t('validation.claude.nameExists');
|
|
37
41
|
}
|
|
38
42
|
|
|
39
|
-
if (!apiKey && !externalCredentialType) {
|
|
43
|
+
if (!apiKey && !externalCredentialType && targetApi !== 'ollama') {
|
|
40
44
|
errors.apiKey = vm.t('validation.claude.apiKeyRequired');
|
|
41
45
|
}
|
|
42
46
|
|
|
@@ -57,6 +61,7 @@ function getClaudeConfigValidationForContext(vm, mode = 'add') {
|
|
|
57
61
|
externalCredentialType,
|
|
58
62
|
baseUrl,
|
|
59
63
|
model,
|
|
64
|
+
targetApi,
|
|
60
65
|
errors,
|
|
61
66
|
ok: !errors.name && !errors.apiKey && !errors.baseUrl && !errors.model
|
|
62
67
|
};
|
|
@@ -88,7 +93,7 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
88
93
|
this.claudeConfigs[name] = this.mergeClaudeConfig(existing, { model });
|
|
89
94
|
this.saveClaudeConfigs();
|
|
90
95
|
this.updateClaudeModelsCurrent();
|
|
91
|
-
if (!this.claudeConfigs[name].apiKey && !this.claudeConfigs[name].externalCredentialType) {
|
|
96
|
+
if (!this.claudeConfigs[name].apiKey && !this.claudeConfigs[name].externalCredentialType && this.claudeConfigs[name].targetApi !== 'ollama') {
|
|
92
97
|
this.showMessage(this.t('toast.claude.apiKeyRequired'), 'error');
|
|
93
98
|
return;
|
|
94
99
|
}
|
|
@@ -115,8 +120,10 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
115
120
|
this.newClaudeConfig = {
|
|
116
121
|
name: '',
|
|
117
122
|
apiKey: config.apiKey || '',
|
|
123
|
+
externalCredentialType: config.externalCredentialType || '',
|
|
118
124
|
baseUrl: config.baseUrl || '',
|
|
119
|
-
model: config.model || ''
|
|
125
|
+
model: config.model || '',
|
|
126
|
+
targetApi: config.targetApi || 'responses'
|
|
120
127
|
};
|
|
121
128
|
this.showAddClaudeConfigKey = false;
|
|
122
129
|
this.showClaudeConfigModal = true;
|
|
@@ -144,7 +151,8 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
144
151
|
apiKey: config.apiKey || '',
|
|
145
152
|
externalCredentialType: config.externalCredentialType || '',
|
|
146
153
|
baseUrl: config.baseUrl || '',
|
|
147
|
-
model: config.model || ''
|
|
154
|
+
model: config.model || '',
|
|
155
|
+
targetApi: config.targetApi || 'responses'
|
|
148
156
|
};
|
|
149
157
|
this.showEditClaudeConfigKey = false;
|
|
150
158
|
this.showEditConfigModal = true;
|
|
@@ -157,8 +165,10 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
157
165
|
}
|
|
158
166
|
const name = validation.name;
|
|
159
167
|
this.editingConfig.apiKey = validation.apiKey;
|
|
168
|
+
this.editingConfig.externalCredentialType = validation.externalCredentialType;
|
|
160
169
|
this.editingConfig.baseUrl = validation.baseUrl;
|
|
161
170
|
this.editingConfig.model = validation.model;
|
|
171
|
+
this.editingConfig.targetApi = validation.targetApi;
|
|
162
172
|
this.claudeConfigs[name] = this.mergeClaudeConfig(this.claudeConfigs[name], this.editingConfig);
|
|
163
173
|
this.saveClaudeConfigs();
|
|
164
174
|
this.showMessage(this.t('toast.operation.success'), 'success');
|
|
@@ -171,7 +181,7 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
171
181
|
closeEditConfigModal() {
|
|
172
182
|
this.showEditConfigModal = false;
|
|
173
183
|
this.showEditClaudeConfigKey = false;
|
|
174
|
-
this.editingConfig = { name: '', apiKey: '', externalCredentialType: '', baseUrl: '', model: '' };
|
|
184
|
+
this.editingConfig = { name: '', apiKey: '', externalCredentialType: '', baseUrl: '', model: '', targetApi: 'responses' };
|
|
175
185
|
},
|
|
176
186
|
|
|
177
187
|
toggleEditClaudeConfigKey() {
|
|
@@ -185,13 +195,15 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
185
195
|
}
|
|
186
196
|
const name = validation.name;
|
|
187
197
|
this.editingConfig.apiKey = validation.apiKey;
|
|
198
|
+
this.editingConfig.externalCredentialType = validation.externalCredentialType;
|
|
188
199
|
this.editingConfig.baseUrl = validation.baseUrl;
|
|
189
200
|
this.editingConfig.model = validation.model;
|
|
201
|
+
this.editingConfig.targetApi = validation.targetApi;
|
|
190
202
|
this.claudeConfigs[name] = this.mergeClaudeConfig(this.claudeConfigs[name], this.editingConfig);
|
|
191
203
|
this.saveClaudeConfigs();
|
|
192
204
|
|
|
193
205
|
const config = this.claudeConfigs[name];
|
|
194
|
-
if (!config.apiKey) {
|
|
206
|
+
if (!config.apiKey && config.targetApi !== 'ollama') {
|
|
195
207
|
this.showMessage(this.t('toast.claude.savedWithoutKey'), 'info');
|
|
196
208
|
this.closeEditConfigModal();
|
|
197
209
|
if (name === this.currentClaudeConfig) {
|
|
@@ -200,9 +212,9 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
200
212
|
return;
|
|
201
213
|
}
|
|
202
214
|
|
|
203
|
-
const _claudeKey = `${name}|${config.apiKey || ""}|${config.baseUrl || ""}|${config.model || ""}`;
|
|
215
|
+
const _claudeKey = `${name}|${config.apiKey || ""}|${config.baseUrl || ""}|${config.model || ""}|${config.targetApi || "responses"}`;
|
|
204
216
|
try {
|
|
205
|
-
const res = await api('apply-claude-config', { config });
|
|
217
|
+
const res = await api('apply-claude-config', { config: { ...config, name } });
|
|
206
218
|
if (res.error || res.success === false) {
|
|
207
219
|
this.showMessage(res.error || this.t('toast.apply.fail'), 'error');
|
|
208
220
|
} else {
|
|
@@ -226,8 +238,10 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
226
238
|
}
|
|
227
239
|
this.newClaudeConfig.name = validation.name;
|
|
228
240
|
this.newClaudeConfig.apiKey = validation.apiKey;
|
|
241
|
+
this.newClaudeConfig.externalCredentialType = validation.externalCredentialType;
|
|
229
242
|
this.newClaudeConfig.baseUrl = validation.baseUrl;
|
|
230
243
|
this.newClaudeConfig.model = validation.model;
|
|
244
|
+
this.newClaudeConfig.targetApi = validation.targetApi;
|
|
231
245
|
const name = validation.name;
|
|
232
246
|
const duplicateName = this.findDuplicateClaudeConfigName(this.newClaudeConfig);
|
|
233
247
|
if (duplicateName) {
|
|
@@ -271,16 +285,16 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
271
285
|
this.refreshClaudeModelContext();
|
|
272
286
|
const config = this.claudeConfigs[name];
|
|
273
287
|
|
|
274
|
-
if (!config.apiKey) {
|
|
288
|
+
if (!config.apiKey && config.targetApi !== 'ollama') {
|
|
275
289
|
if (config.externalCredentialType) {
|
|
276
290
|
return this.showMessage(this.t('toast.claude.externalAuth'), 'info');
|
|
277
291
|
}
|
|
278
292
|
return this.showMessage(this.t('toast.claude.apiKeyRequired'), 'error');
|
|
279
293
|
}
|
|
280
294
|
|
|
281
|
-
const _claudeKey2 = `${name}|${config.apiKey || ""}|${config.baseUrl || ""}|${config.model || ""}`;
|
|
295
|
+
const _claudeKey2 = `${name}|${config.apiKey || ""}|${config.baseUrl || ""}|${config.model || ""}|${config.targetApi || "responses"}`;
|
|
282
296
|
try {
|
|
283
|
-
const res = await api('apply-claude-config', { config });
|
|
297
|
+
const res = await api('apply-claude-config', { config: { ...config, name } });
|
|
284
298
|
if (res.error || res.success === false) {
|
|
285
299
|
this.showMessage(res.error || this.t('toast.apply.fail'), 'error');
|
|
286
300
|
} else {
|
|
@@ -300,8 +314,10 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
300
314
|
this.newClaudeConfig = {
|
|
301
315
|
name: '',
|
|
302
316
|
apiKey: '',
|
|
317
|
+
externalCredentialType: '',
|
|
303
318
|
baseUrl: '',
|
|
304
|
-
model: ''
|
|
319
|
+
model: '',
|
|
320
|
+
targetApi: 'responses'
|
|
305
321
|
};
|
|
306
322
|
},
|
|
307
323
|
|
|
@@ -89,7 +89,7 @@ export function createAppMethods() {
|
|
|
89
89
|
api,
|
|
90
90
|
defaultOpenclawTemplate: DEFAULT_OPENCLAW_TEMPLATE
|
|
91
91
|
}),
|
|
92
|
-
...createInstallMethods(),
|
|
92
|
+
...createInstallMethods({ api }),
|
|
93
93
|
...createRuntimeMethods({ api }),
|
|
94
94
|
...createTaskOrchestrationMethods({ api })
|
|
95
95
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export function createInstallMethods() {
|
|
1
|
+
export function createInstallMethods(options = {}) {
|
|
2
|
+
const { api } = options;
|
|
2
3
|
return {
|
|
3
4
|
normalizeInstallPackageManager(value) {
|
|
4
5
|
const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
@@ -16,6 +17,133 @@ export function createInstallMethods() {
|
|
|
16
17
|
return 'install';
|
|
17
18
|
},
|
|
18
19
|
|
|
20
|
+
normalizePackageVersion(value) {
|
|
21
|
+
const normalized = typeof value === 'string' ? value.trim().replace(/^v/i, '') : '';
|
|
22
|
+
return /^\d+(?:\.\d+){0,2}(?:[-+][0-9A-Za-z.-]+)?$/.test(normalized) ? normalized : '';
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
comparePackageVersions(left, right) {
|
|
26
|
+
const normalizeParts = (value) => {
|
|
27
|
+
const normalized = this.normalizePackageVersion(value);
|
|
28
|
+
if (!normalized) return null;
|
|
29
|
+
return normalized.split(/[+-]/)[0].split('.').map((part) => Number.parseInt(part, 10) || 0);
|
|
30
|
+
};
|
|
31
|
+
const a = normalizeParts(left);
|
|
32
|
+
const b = normalizeParts(right);
|
|
33
|
+
if (!a || !b) return 0;
|
|
34
|
+
for (let i = 0; i < 3; i += 1) {
|
|
35
|
+
const diff = (a[i] || 0) - (b[i] || 0);
|
|
36
|
+
if (diff < 0) return -1;
|
|
37
|
+
if (diff > 0) return 1;
|
|
38
|
+
}
|
|
39
|
+
return 0;
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
isAppUpdateAvailable() {
|
|
43
|
+
const current = this.normalizePackageVersion(this.appVersion);
|
|
44
|
+
const latest = this.normalizePackageVersion(this.appLatestVersion);
|
|
45
|
+
if (!current || !latest) return false;
|
|
46
|
+
return this.comparePackageVersions(current, latest) < 0;
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
isAppVersionStatusVisible() {
|
|
50
|
+
return !!(this.appVersion || this.appLatestVersion || this.appVersionStatusLoading || this.appVersionStatusChecked || this.appVersionStatusError);
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
appVersionStatusKind() {
|
|
54
|
+
if (this.appVersionStatusLoading) return 'loading';
|
|
55
|
+
if (this.appVersionStatusError) return 'error';
|
|
56
|
+
if (this.isAppUpdateAvailable()) return 'available';
|
|
57
|
+
if (this.appVersionStatusChecked) return 'current';
|
|
58
|
+
return 'idle';
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
appUpdateNoticeText() {
|
|
62
|
+
if (this.appVersionStatusLoading) return this.t('side.update.checking');
|
|
63
|
+
if (this.appVersionStatusError) return this.t('side.update.retry');
|
|
64
|
+
const latest = this.normalizePackageVersion(this.appLatestVersion);
|
|
65
|
+
if (this.isAppUpdateAvailable()) return latest
|
|
66
|
+
? this.t('side.update.availableWithVersion', { version: latest })
|
|
67
|
+
: this.t('side.update.available');
|
|
68
|
+
if (this.appVersionStatusChecked) return this.t('side.update.upToDate');
|
|
69
|
+
return this.t('side.update.check');
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
appUpdateNoticeMeta() {
|
|
73
|
+
if (this.appVersionStatusLoading) return this.t('side.update.checkingMeta');
|
|
74
|
+
if (this.appVersionStatusError) return this.appVersionStatusError;
|
|
75
|
+
const current = this.normalizePackageVersion(this.appVersion);
|
|
76
|
+
const latest = this.normalizePackageVersion(this.appLatestVersion);
|
|
77
|
+
if (current && latest) {
|
|
78
|
+
return this.t('side.update.metaVersions', { current, latest });
|
|
79
|
+
}
|
|
80
|
+
if (current) {
|
|
81
|
+
return this.t('side.update.currentOnly', { current });
|
|
82
|
+
}
|
|
83
|
+
return this.t('side.update.meta');
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
appVersionStatusTitle() {
|
|
87
|
+
const source = typeof this.appVersionStatusSource === 'string' ? this.appVersionStatusSource.trim() : '';
|
|
88
|
+
const checkedAt = typeof this.appVersionStatusCheckedAt === 'string' ? this.appVersionStatusCheckedAt.trim() : '';
|
|
89
|
+
const suffix = [source, checkedAt].filter(Boolean).join(' · ');
|
|
90
|
+
const meta = this.appUpdateNoticeMeta();
|
|
91
|
+
return suffix ? `${meta} · ${suffix}` : meta;
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
handleAppVersionStatusClick() {
|
|
95
|
+
if (this.isAppUpdateAvailable()) {
|
|
96
|
+
this.openAppUpdateDocs();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
void this.loadAppVersionStatus({ silent: false, force: true });
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
async loadAppVersionStatus(options = {}) {
|
|
103
|
+
if (typeof api !== 'function') return false;
|
|
104
|
+
if (this.appVersionStatusLoading) return false;
|
|
105
|
+
this.appVersionStatusLoading = true;
|
|
106
|
+
this.appVersionStatusError = '';
|
|
107
|
+
try {
|
|
108
|
+
const res = await api('version-status', options.force ? { force: true } : {});
|
|
109
|
+
if (res && res.currentVersion && !this.appVersion) {
|
|
110
|
+
this.appVersion = this.normalizePackageVersion(res.currentVersion) || String(res.currentVersion || '');
|
|
111
|
+
}
|
|
112
|
+
if (res && res.latestVersion) {
|
|
113
|
+
this.appLatestVersion = this.normalizePackageVersion(res.latestVersion) || String(res.latestVersion || '');
|
|
114
|
+
}
|
|
115
|
+
if (res && typeof res.source === 'string') {
|
|
116
|
+
this.appVersionStatusSource = res.source;
|
|
117
|
+
}
|
|
118
|
+
if (res && typeof res.checkedAt === 'string') {
|
|
119
|
+
this.appVersionStatusCheckedAt = res.checkedAt;
|
|
120
|
+
}
|
|
121
|
+
if (res && res.error) {
|
|
122
|
+
this.appVersionStatusError = res.error;
|
|
123
|
+
this.appVersionStatusChecked = true;
|
|
124
|
+
if (!options.silent) this.showMessage(res.error, 'error');
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
this.appVersionStatusChecked = true;
|
|
128
|
+
return true;
|
|
129
|
+
} catch (e) {
|
|
130
|
+
const message = e && e.message ? e.message : this.t('side.update.checkFailed');
|
|
131
|
+
this.appVersionStatusError = message;
|
|
132
|
+
this.appVersionStatusChecked = true;
|
|
133
|
+
if (!options.silent) this.showMessage(message, 'error');
|
|
134
|
+
return false;
|
|
135
|
+
} finally {
|
|
136
|
+
this.appVersionStatusLoading = false;
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
openAppUpdateDocs() {
|
|
141
|
+
this.installCommandAction = 'update';
|
|
142
|
+
if (typeof this.switchMainTab === 'function') {
|
|
143
|
+
this.switchMainTab('docs');
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
|
|
19
147
|
normalizeInstallRegistryPreset(value) {
|
|
20
148
|
const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
21
149
|
if (normalized === 'default' || normalized === 'npmmirror' || normalized === 'tencent' || normalized === 'custom') {
|
|
@@ -412,11 +412,16 @@ export function createSessionActionMethods(options = {}) {
|
|
|
412
412
|
const model = typeof payload.model === 'string' && payload.model.trim()
|
|
413
413
|
? payload.model.trim()
|
|
414
414
|
: 'glm-4.7';
|
|
415
|
-
|
|
415
|
+
const targetApiRaw = typeof payload.targetApi === 'string' ? payload.targetApi.trim().toLowerCase() : '';
|
|
416
|
+
const targetApi = targetApiRaw === 'chat_completions' || targetApiRaw === 'chat-completions' || targetApiRaw === 'chat/completions'
|
|
417
|
+
? 'chat_completions'
|
|
418
|
+
: (targetApiRaw === 'ollama' ? 'ollama' : 'responses');
|
|
419
|
+
if (!baseUrl || (!apiKey && targetApi !== 'ollama')) return '';
|
|
416
420
|
const urlArg = this.quoteShellArg(baseUrl);
|
|
417
421
|
const keyArg = this.quoteShellArg(apiKey);
|
|
418
422
|
const modelArg = this.quoteShellArg(model);
|
|
419
|
-
|
|
423
|
+
const targetArg = targetApi !== 'responses' ? ` --target-api ${this.quoteShellArg(targetApi)}` : '';
|
|
424
|
+
return `${this.getShareCommandPrefixInvocation()} claude ${urlArg} ${keyArg} ${modelArg}${targetArg}`;
|
|
420
425
|
},
|
|
421
426
|
|
|
422
427
|
async copyProviderShareCommand(provider) {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
findDuplicateClaudeConfigName,
|
|
3
3
|
getClaudeModelCatalogForBaseUrl,
|
|
4
|
+
isLikelyBuiltinClaudeProxySettingsEnv,
|
|
5
|
+
matchBuiltinClaudeProxyConfigFromSettings,
|
|
4
6
|
matchClaudeConfigFromSettings,
|
|
5
7
|
normalizeClaudeConfig,
|
|
6
8
|
normalizeClaudeSettingsEnv,
|
|
@@ -241,6 +243,14 @@ export function createStartupClaudeMethods(options = {}) {
|
|
|
241
243
|
return matchClaudeConfigFromSettings(this.claudeConfigs, env);
|
|
242
244
|
},
|
|
243
245
|
|
|
246
|
+
matchBuiltinClaudeProxyConfigFromSettings(env) {
|
|
247
|
+
return matchBuiltinClaudeProxyConfigFromSettings(this.claudeConfigs, env, this.currentClaudeConfig);
|
|
248
|
+
},
|
|
249
|
+
|
|
250
|
+
shouldSuppressClaudeSettingsImport(env) {
|
|
251
|
+
return isLikelyBuiltinClaudeProxySettingsEnv(env);
|
|
252
|
+
},
|
|
253
|
+
|
|
244
254
|
findDuplicateClaudeConfigName(config) {
|
|
245
255
|
return findDuplicateClaudeConfigName(this.claudeConfigs, config);
|
|
246
256
|
},
|
|
@@ -256,7 +266,8 @@ export function createStartupClaudeMethods(options = {}) {
|
|
|
256
266
|
baseUrl: next.baseUrl,
|
|
257
267
|
model: next.model || previous.model || 'glm-4.7',
|
|
258
268
|
hasKey: !!(next.apiKey || externalCredentialType),
|
|
259
|
-
externalCredentialType
|
|
269
|
+
externalCredentialType,
|
|
270
|
+
targetApi: next.targetApi || previous.targetApi || 'responses'
|
|
260
271
|
};
|
|
261
272
|
},
|
|
262
273
|
|
|
@@ -332,7 +343,8 @@ export function createStartupClaudeMethods(options = {}) {
|
|
|
332
343
|
}
|
|
333
344
|
return;
|
|
334
345
|
}
|
|
335
|
-
const
|
|
346
|
+
const settingsEnv = (res && res.env) || {};
|
|
347
|
+
const matchName = this.matchClaudeConfigFromSettings(settingsEnv);
|
|
336
348
|
if (matchName) {
|
|
337
349
|
if (this.currentClaudeConfig !== matchName) {
|
|
338
350
|
this.currentClaudeConfig = matchName;
|
|
@@ -341,7 +353,18 @@ export function createStartupClaudeMethods(options = {}) {
|
|
|
341
353
|
this.refreshClaudeModelContext({ silentError: silentModelError });
|
|
342
354
|
return;
|
|
343
355
|
}
|
|
344
|
-
const
|
|
356
|
+
const builtinProxyMatch = this.matchBuiltinClaudeProxyConfigFromSettings(settingsEnv);
|
|
357
|
+
if (builtinProxyMatch) {
|
|
358
|
+
if (this.currentClaudeConfig !== builtinProxyMatch) {
|
|
359
|
+
this.currentClaudeConfig = builtinProxyMatch;
|
|
360
|
+
try { localStorage.setItem('currentClaudeConfig', builtinProxyMatch); } catch (_) {}
|
|
361
|
+
}
|
|
362
|
+
this.refreshClaudeModelContext({ silentError: silentModelError });
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
const importedName = this.shouldSuppressClaudeSettingsImport(settingsEnv)
|
|
366
|
+
? ''
|
|
367
|
+
: this.ensureClaudeConfigFromSettings(settingsEnv);
|
|
345
368
|
if (importedName) {
|
|
346
369
|
if (this.currentClaudeConfig !== importedName) {
|
|
347
370
|
this.currentClaudeConfig = importedName;
|
|
@@ -145,6 +145,17 @@ const en = Object.freeze({
|
|
|
145
145
|
'side.overview.doctor.meta': 'Overview / Diagnostics',
|
|
146
146
|
'side.docs.cliInstall': 'CLI Install',
|
|
147
147
|
'side.docs.cliInstall.meta': 'Install / Update / Uninstall',
|
|
148
|
+
'side.update.available': 'Update available',
|
|
149
|
+
'side.update.availableWithVersion': 'Update available v{version}',
|
|
150
|
+
'side.update.meta': 'Open update command',
|
|
151
|
+
'side.update.metaVersions': 'Current v{current} → latest v{latest}',
|
|
152
|
+
'side.update.checkFailed': 'Failed to check latest version',
|
|
153
|
+
'side.update.check': 'Check for updates',
|
|
154
|
+
'side.update.checking': 'Checking for updates…',
|
|
155
|
+
'side.update.checkingMeta': 'Contacting npm registry',
|
|
156
|
+
'side.update.upToDate': 'Up to date',
|
|
157
|
+
'side.update.currentOnly': 'Current v{current}',
|
|
158
|
+
'side.update.retry': 'Retry version check',
|
|
148
159
|
'side.config.codex': 'Codex',
|
|
149
160
|
'side.config.codex.meta': 'Provider / Model',
|
|
150
161
|
'side.config.claude': 'Claude Code',
|
|
@@ -1138,6 +1149,13 @@ const en = Object.freeze({
|
|
|
1138
1149
|
'claude.model': 'Model',
|
|
1139
1150
|
'claude.model.placeholder': 'e.g. claude-3-7-sonnet',
|
|
1140
1151
|
'claude.model.hint': 'Model changes are saved and applied to the current config automatically.',
|
|
1152
|
+
'claude.targetApi.label': 'Target API',
|
|
1153
|
+
'claude.targetApi.responses': 'Anthropic',
|
|
1154
|
+
'claude.targetApi.chatCompletions': 'OpenAI Chat Completions (/v1/chat/completions)',
|
|
1155
|
+
'claude.targetApi.ollama': 'Ollama Chat (/api/chat)',
|
|
1156
|
+
'claude.targetApi.chatCompletionsBadge': 'OpenAI Chat Completions',
|
|
1157
|
+
'claude.targetApi.ollamaBadge': 'Ollama',
|
|
1158
|
+
'claude.targetApi.hint': 'When Chat Completions or Ollama is selected, the Claude-compatible proxy performs the built-in conversion without changing the Codex provider wire_api; Ollama does not require an API key.',
|
|
1141
1159
|
'claude.health.title': 'Config health check',
|
|
1142
1160
|
'claude.health.run': 'Run check',
|
|
1143
1161
|
'claude.health.running': 'Checking...',
|
|
@@ -146,6 +146,17 @@ const ja = Object.freeze({
|
|
|
146
146
|
'side.overview.doctor.meta': '概要 / 診断 / ジャンプ',
|
|
147
147
|
'side.docs.cliInstall': 'CLI インストール',
|
|
148
148
|
'side.docs.cliInstall.meta': 'インストール / 更新 / 削除',
|
|
149
|
+
'side.update.available': '新しいバージョンがあります',
|
|
150
|
+
'side.update.availableWithVersion': '新しいバージョン v{version}',
|
|
151
|
+
'side.update.meta': '更新コマンドを開く',
|
|
152
|
+
'side.update.metaVersions': '現在 v{current} → 最新 v{latest}',
|
|
153
|
+
'side.update.checkFailed': '最新バージョンの確認に失敗しました',
|
|
154
|
+
'side.update.check': '更新を確認',
|
|
155
|
+
'side.update.checking': '更新を確認中…',
|
|
156
|
+
'side.update.checkingMeta': 'npm registry に接続中',
|
|
157
|
+
'side.update.upToDate': '最新です',
|
|
158
|
+
'side.update.currentOnly': '現在 v{current}',
|
|
159
|
+
'side.update.retry': 'バージョン確認を再試行',
|
|
149
160
|
'side.config.codex': 'Codex',
|
|
150
161
|
'side.config.codex.meta': 'Provider / Model',
|
|
151
162
|
'side.config.claude': 'Claude Code',
|
|
@@ -1131,6 +1142,13 @@ const ja = Object.freeze({
|
|
|
1131
1142
|
'claude.model': 'モデル',
|
|
1132
1143
|
'claude.model.placeholder': '例: claude-3-7-sonnet',
|
|
1133
1144
|
'claude.model.hint': 'モデル変更後は自動保存され、現在の設定に適用されます。',
|
|
1145
|
+
'claude.targetApi.label': 'ターゲット API',
|
|
1146
|
+
'claude.targetApi.responses': 'Anthropic',
|
|
1147
|
+
'claude.targetApi.chatCompletions': 'OpenAI Chat Completions (/v1/chat/completions)',
|
|
1148
|
+
'claude.targetApi.ollama': 'Ollama Chat (/api/chat)',
|
|
1149
|
+
'claude.targetApi.chatCompletionsBadge': 'OpenAI Chat Completions',
|
|
1150
|
+
'claude.targetApi.ollamaBadge': 'Ollama',
|
|
1151
|
+
'claude.targetApi.hint': 'Chat Completions または Ollama を選ぶと Claude 互換プロキシが内蔵変換します。Codex provider の wire_api は変更しません。Ollama では API Key は不要です。',
|
|
1134
1152
|
'claude.health.title': '設定ヘルスチェック',
|
|
1135
1153
|
'claude.health.run': 'チェック実行',
|
|
1136
1154
|
'claude.health.running': 'チェック中...',
|
|
@@ -106,6 +106,17 @@ const vi = Object.freeze({
|
|
|
106
106
|
'side.overview.doctor.meta': 'Tổng quan / Chẩn đoán',
|
|
107
107
|
'side.docs.cliInstall': 'Cài CLI',
|
|
108
108
|
'side.docs.cliInstall.meta': 'Cài đặt / Cập nhật / Gỡ',
|
|
109
|
+
'side.update.available': 'Có bản cập nhật',
|
|
110
|
+
'side.update.availableWithVersion': 'Có bản cập nhật v{version}',
|
|
111
|
+
'side.update.meta': 'Mở lệnh cập nhật',
|
|
112
|
+
'side.update.metaVersions': 'Hiện tại v{current} → mới nhất v{latest}',
|
|
113
|
+
'side.update.checkFailed': 'Không thể kiểm tra phiên bản mới nhất',
|
|
114
|
+
'side.update.check': 'Kiểm tra cập nhật',
|
|
115
|
+
'side.update.checking': 'Đang kiểm tra cập nhật…',
|
|
116
|
+
'side.update.checkingMeta': 'Đang kết nối npm registry',
|
|
117
|
+
'side.update.upToDate': 'Đã là phiên bản mới nhất',
|
|
118
|
+
'side.update.currentOnly': 'Hiện tại v{current}',
|
|
119
|
+
'side.update.retry': 'Thử kiểm tra phiên bản lại',
|
|
109
120
|
'side.config.codex': 'Codex',
|
|
110
121
|
'side.config.codex.meta': 'Provider / Model',
|
|
111
122
|
'side.config.claude': 'Claude Code',
|
|
@@ -237,6 +248,15 @@ const vi = Object.freeze({
|
|
|
237
248
|
'dashboard.doctor.checking': 'Đang kiểm tra...',
|
|
238
249
|
'dashboard.doctor.export': 'Xuất báo cáo',
|
|
239
250
|
|
|
251
|
+
// Claude target API
|
|
252
|
+
'claude.targetApi.label': 'API đích',
|
|
253
|
+
'claude.targetApi.responses': 'Anthropic',
|
|
254
|
+
'claude.targetApi.chatCompletions': 'OpenAI Chat Completions (/v1/chat/completions)',
|
|
255
|
+
'claude.targetApi.ollama': 'Ollama Chat (/api/chat)',
|
|
256
|
+
'claude.targetApi.chatCompletionsBadge': 'OpenAI Chat Completions',
|
|
257
|
+
'claude.targetApi.ollamaBadge': 'Ollama',
|
|
258
|
+
'claude.targetApi.hint': 'Khi chọn Chat Completions hoặc Ollama, proxy tương thích Claude sẽ chuyển đổi nội bộ mà không thay đổi wire_api của Codex provider; Ollama không cần API key.',
|
|
259
|
+
|
|
240
260
|
// Toasts
|
|
241
261
|
'toast.copy.empty': 'Không có gì để sao chép',
|
|
242
262
|
'toast.copy.ok': 'Đã sao chép',
|
|
@@ -145,6 +145,17 @@ const zh = Object.freeze({
|
|
|
145
145
|
'side.overview.doctor.meta': '总览 / 诊断 / 跳转',
|
|
146
146
|
'side.docs.cliInstall': 'CLI 安装',
|
|
147
147
|
'side.docs.cliInstall.meta': '安装 / 升级 / 卸载',
|
|
148
|
+
'side.update.available': '有新版本',
|
|
149
|
+
'side.update.availableWithVersion': '有新版本 v{version}',
|
|
150
|
+
'side.update.meta': '点击查看更新命令',
|
|
151
|
+
'side.update.metaVersions': '当前 v{current} → 最新 v{latest}',
|
|
152
|
+
'side.update.checkFailed': '检查最新版本失败',
|
|
153
|
+
'side.update.check': '检查更新',
|
|
154
|
+
'side.update.checking': '正在检查更新…',
|
|
155
|
+
'side.update.checkingMeta': '正在连接 npm registry',
|
|
156
|
+
'side.update.upToDate': '已是最新',
|
|
157
|
+
'side.update.currentOnly': '当前 v{current}',
|
|
158
|
+
'side.update.retry': '重试版本检查',
|
|
148
159
|
'side.config.codex': 'Codex',
|
|
149
160
|
'side.config.codex.meta': 'Provider / Model',
|
|
150
161
|
'side.config.claude': 'Claude Code',
|
|
@@ -1141,6 +1152,13 @@ const zh = Object.freeze({
|
|
|
1141
1152
|
'claude.model': '模型',
|
|
1142
1153
|
'claude.model.placeholder': '例如: claude-3-7-sonnet',
|
|
1143
1154
|
'claude.model.hint': '模型修改后会自动保存并应用到当前配置。',
|
|
1155
|
+
'claude.targetApi.label': '目标 API',
|
|
1156
|
+
'claude.targetApi.responses': 'Anthropic',
|
|
1157
|
+
'claude.targetApi.chatCompletions': 'OpenAI Chat Completions (/v1/chat/completions)',
|
|
1158
|
+
'claude.targetApi.ollama': 'Ollama Chat (/api/chat)',
|
|
1159
|
+
'claude.targetApi.chatCompletionsBadge': 'OpenAI Chat Completions',
|
|
1160
|
+
'claude.targetApi.ollamaBadge': 'Ollama',
|
|
1161
|
+
'claude.targetApi.hint': '选择 Chat Completions 或 Ollama 时由 Claude 兼容代理内建转换,不修改 Codex provider 的 wire_api;Ollama 可不填 API Key。',
|
|
1144
1162
|
'claude.health.title': '配置健康检查',
|
|
1145
1163
|
'claude.health.run': '运行检查',
|
|
1146
1164
|
'claude.health.running': '检查中...',
|
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
<div id="app" class="container" v-cloak>
|
|
2
|
+
<div v-if="!sessionStandalone" class="mobile-brand-bar">
|
|
3
|
+
<div class="mobile-brand-title">Codex Mate<span v-if="appVersion" class="brand-version"> v{{ appVersion }}</span></div>
|
|
4
|
+
<button
|
|
5
|
+
v-if="isAppVersionStatusVisible()"
|
|
6
|
+
type="button"
|
|
7
|
+
:class="['mobile-update-chip', 'mobile-update-chip--' + appVersionStatusKind()]"
|
|
8
|
+
:title="appVersionStatusTitle()"
|
|
9
|
+
@click="handleAppVersionStatusClick">
|
|
10
|
+
<span class="side-update-dot"></span>
|
|
11
|
+
<span class="mobile-update-text">{{ appUpdateNoticeText() }}</span>
|
|
12
|
+
</button>
|
|
13
|
+
</div>
|
|
2
14
|
<div v-if="!sessionStandalone" class="top-tabs" role="tablist" :aria-label="t('nav.topTabs.aria')">
|
|
3
15
|
<button type="button" class="top-tab"
|
|
4
16
|
id="tab-dashboard"
|
|
@@ -110,6 +122,18 @@
|
|
|
110
122
|
<div class="brand-kicker">Codex Mate<span v-if="appVersion" class="brand-version"> v{{ appVersion }}</span></div>
|
|
111
123
|
</div>
|
|
112
124
|
</div>
|
|
125
|
+
<button
|
|
126
|
+
v-if="isAppVersionStatusVisible()"
|
|
127
|
+
type="button"
|
|
128
|
+
:class="['side-update-notice', 'side-update-notice--' + appVersionStatusKind()]"
|
|
129
|
+
:title="appVersionStatusTitle()"
|
|
130
|
+
@click="handleAppVersionStatusClick">
|
|
131
|
+
<span class="side-update-dot"></span>
|
|
132
|
+
<span class="side-update-copy">
|
|
133
|
+
<span class="side-update-title">{{ appUpdateNoticeText() }}</span>
|
|
134
|
+
<span class="side-update-meta">{{ appUpdateNoticeMeta() }}</span>
|
|
135
|
+
</span>
|
|
136
|
+
</button>
|
|
113
137
|
</div>
|
|
114
138
|
|
|
115
139
|
<div class="side-rail-nav">
|
|
@@ -165,6 +165,15 @@
|
|
|
165
165
|
<input v-model="newClaudeConfig.model" :class="['form-input', { invalid: !!claudeConfigFieldError('add', 'model') }]" :placeholder="t('placeholder.modelExample')" autocomplete="off" spellcheck="false">
|
|
166
166
|
<div v-if="claudeConfigFieldError('add', 'model')" class="form-hint form-error">{{ claudeConfigFieldError('add', 'model') }}</div>
|
|
167
167
|
</div>
|
|
168
|
+
<div class="form-group">
|
|
169
|
+
<label class="form-label">{{ t('claude.targetApi.label') }}</label>
|
|
170
|
+
<select v-model="newClaudeConfig.targetApi" class="form-input">
|
|
171
|
+
<option value="responses">{{ t('claude.targetApi.responses') }}</option>
|
|
172
|
+
<option value="chat_completions">{{ t('claude.targetApi.chatCompletions') }}</option>
|
|
173
|
+
<option value="ollama">{{ t('claude.targetApi.ollama') }}</option>
|
|
174
|
+
</select>
|
|
175
|
+
<div class="form-hint">{{ t('claude.targetApi.hint') }}</div>
|
|
176
|
+
</div>
|
|
168
177
|
|
|
169
178
|
<div class="btn-group">
|
|
170
179
|
<button class="btn btn-cancel" @click="closeClaudeConfigModal">{{ t('common.cancel') }}</button>
|
|
@@ -204,6 +213,15 @@
|
|
|
204
213
|
<input v-model="editingConfig.model" :class="['form-input', { invalid: !!claudeConfigFieldError('edit', 'model') }]" :placeholder="t('placeholder.modelExample')" autocomplete="off" spellcheck="false">
|
|
205
214
|
<div v-if="claudeConfigFieldError('edit', 'model')" class="form-hint form-error">{{ claudeConfigFieldError('edit', 'model') }}</div>
|
|
206
215
|
</div>
|
|
216
|
+
<div class="form-group">
|
|
217
|
+
<label class="form-label">{{ t('claude.targetApi.label') }}</label>
|
|
218
|
+
<select v-model="editingConfig.targetApi" class="form-input">
|
|
219
|
+
<option value="responses">{{ t('claude.targetApi.responses') }}</option>
|
|
220
|
+
<option value="chat_completions">{{ t('claude.targetApi.chatCompletions') }}</option>
|
|
221
|
+
<option value="ollama">{{ t('claude.targetApi.ollama') }}</option>
|
|
222
|
+
</select>
|
|
223
|
+
<div class="form-hint">{{ t('claude.targetApi.hint') }}</div>
|
|
224
|
+
</div>
|
|
207
225
|
|
|
208
226
|
<div class="btn-group">
|
|
209
227
|
<button class="btn btn-cancel" @click="closeEditConfigModal">{{ t('common.cancel') }}</button>
|
|
@@ -260,4 +278,3 @@
|
|
|
260
278
|
</div>
|
|
261
279
|
</div>
|
|
262
280
|
</div>
|
|
263
|
-
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
<label class="settings-toggle-row tool-config-write-toggle">
|
|
35
35
|
<input
|
|
36
36
|
type="checkbox"
|
|
37
|
+
autocomplete="off"
|
|
37
38
|
:checked="isToolConfigWriteAllowed('claude')"
|
|
38
39
|
:disabled="toolConfigPermissionSaving.claude"
|
|
39
40
|
@change="setToolConfigPermission('claude', $event.target.checked)">
|
|
@@ -145,10 +146,12 @@
|
|
|
145
146
|
</div>
|
|
146
147
|
<div v-for="(config, name) in claudeConfigs" :key="name" :class="['card', { active: currentClaudeConfig === name }]" @click="applyClaudeConfig(name)" @keydown.enter.self.prevent="applyClaudeConfig(name)" @keydown.space.self.prevent="applyClaudeConfig(name)" tabindex="0" role="button" :aria-current="currentClaudeConfig === name ? 'true' : null">
|
|
147
148
|
<div class="card-leading">
|
|
148
|
-
<div class="card-icon">{{ name.charAt(0).toUpperCase() }}
|
|
149
|
+
<div class="card-icon">{{ name.charAt(0).toUpperCase() }}<span v-if="config.targetApi === 'chat_completions' || config.targetApi === 'ollama'" class="card-icon-dot" :title="t('config.transformProvider.title')"></span></div>
|
|
149
150
|
<div class="card-content">
|
|
150
151
|
<div class="card-title">{{ name }}</div>
|
|
151
152
|
<div class="card-subtitle card-subtitle-model">{{ config.model || t('claude.model.unset') }}</div>
|
|
153
|
+
<div class="card-subtitle" v-if="config.targetApi === 'chat_completions'">{{ t('claude.targetApi.chatCompletionsBadge') }}</div>
|
|
154
|
+
<div class="card-subtitle" v-else-if="config.targetApi === 'ollama'">{{ t('claude.targetApi.ollamaBadge') }}</div>
|
|
152
155
|
<div class="card-subtitle card-subtitle-url" v-if="config.baseUrl">{{ config.baseUrl }}</div>
|
|
153
156
|
</div>
|
|
154
157
|
</div>
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
<label class="settings-toggle-row tool-config-write-toggle">
|
|
35
35
|
<input
|
|
36
36
|
type="checkbox"
|
|
37
|
+
autocomplete="off"
|
|
37
38
|
:checked="isToolConfigWriteAllowed('codex')"
|
|
38
39
|
:disabled="toolConfigPermissionSaving.codex"
|
|
39
40
|
@change="setToolConfigPermission('codex', $event.target.checked)">
|
|
@@ -203,4 +204,4 @@
|
|
|
203
204
|
</div>
|
|
204
205
|
</div>
|
|
205
206
|
</template>
|
|
206
|
-
</div>
|
|
207
|
+
</div>
|