codexmate 0.0.43 → 0.0.45
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 +36 -3
- package/web-ui/index.html +1 -0
- package/web-ui/logic.claude.mjs +65 -2
- package/web-ui/logic.runtime.mjs +0 -7
- package/web-ui/modules/app.computed.index.mjs +3 -1
- package/web-ui/modules/app.computed.main-tabs.mjs +3 -0
- package/web-ui/modules/app.computed.prompts.mjs +28 -0
- package/web-ui/modules/app.computed.session.mjs +23 -1
- package/web-ui/modules/app.methods.agents.mjs +50 -4
- 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.navigation.mjs +2 -1
- package/web-ui/modules/app.methods.session-actions.mjs +17 -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 +42 -5
- package/web-ui/modules/i18n/locales/ja.mjs +42 -5
- package/web-ui/modules/i18n/locales/vi.mjs +51 -0
- package/web-ui/modules/i18n/locales/zh.mjs +42 -5
- package/web-ui/partials/index/layout-footer.html +1 -1
- package/web-ui/partials/index/layout-header.html +64 -0
- package/web-ui/partials/index/modal-config-template-agents.html +12 -13
- package/web-ui/partials/index/modals-basic.html +18 -1
- package/web-ui/partials/index/panel-config-claude.html +4 -7
- package/web-ui/partials/index/panel-config-codex.html +2 -6
- package/web-ui/partials/index/panel-prompts.html +100 -0
- package/web-ui/partials/index/panel-sessions.html +30 -10
- package/web-ui/partials/index/panel-usage.html +34 -18
- package/web-ui/res/web-ui-render.precompiled.js +579 -149
- package/web-ui/styles/controls-forms.css +5 -5
- package/web-ui/styles/layout-shell.css +145 -0
- package/web-ui/styles/modals-core.css +162 -0
- package/web-ui/styles/responsive.css +77 -5
- package/web-ui/styles/sessions-toolbar-trash.css +45 -10
- package/web-ui/styles/sessions-usage.css +31 -2
package/web-ui/logic.runtime.mjs
CHANGED
|
@@ -101,10 +101,6 @@ export function shouldForceCompactLayoutMode(options = {}) {
|
|
|
101
101
|
const screenHeight = Number(options.screenHeight || 0);
|
|
102
102
|
const shortEdge = Number(options.shortEdge || (screenWidth > 0 && screenHeight > 0 ? Math.min(screenWidth, screenHeight) : 0));
|
|
103
103
|
const maxTouchPoints = Number(options.maxTouchPoints || 0);
|
|
104
|
-
const userAgent = typeof options.userAgent === 'string' ? options.userAgent : '';
|
|
105
|
-
const isMobileUa = typeof options.isMobileUa === 'boolean'
|
|
106
|
-
? options.isMobileUa
|
|
107
|
-
: /(Android|iPhone|iPad|iPod|Mobile)/i.test(userAgent);
|
|
108
104
|
const coarsePointer = !!options.coarsePointer;
|
|
109
105
|
const noHover = !!options.noHover;
|
|
110
106
|
const isSmallPhysicalScreen = shortEdge > 0 && shortEdge <= 920;
|
|
@@ -115,9 +111,6 @@ export function shouldForceCompactLayoutMode(options = {}) {
|
|
|
115
111
|
if (!isNarrowViewport) {
|
|
116
112
|
return false;
|
|
117
113
|
}
|
|
118
|
-
if (isMobileUa) {
|
|
119
|
-
return true;
|
|
120
|
-
}
|
|
121
114
|
return pointerSuggestsTouchOnly && maxTouchPoints > 0;
|
|
122
115
|
}
|
|
123
116
|
|
|
@@ -4,6 +4,7 @@ import { createSessionComputed } from './app.computed.session.mjs';
|
|
|
4
4
|
import { createConfigModeComputed } from './config-mode.computed.mjs';
|
|
5
5
|
import { createSkillsComputed } from './skills.computed.mjs';
|
|
6
6
|
import { createPluginsComputed } from './plugins.computed.mjs';
|
|
7
|
+
import { createPromptsComputed } from './app.computed.prompts.mjs';
|
|
7
8
|
|
|
8
9
|
export function createAppComputed() {
|
|
9
10
|
return {
|
|
@@ -12,6 +13,7 @@ export function createAppComputed() {
|
|
|
12
13
|
...createMainTabsComputed(),
|
|
13
14
|
...createSkillsComputed(),
|
|
14
15
|
...createPluginsComputed(),
|
|
15
|
-
...createConfigModeComputed()
|
|
16
|
+
...createConfigModeComputed(),
|
|
17
|
+
...createPromptsComputed()
|
|
16
18
|
};
|
|
17
19
|
}
|
|
@@ -151,6 +151,7 @@ export function createMainTabsComputed() {
|
|
|
151
151
|
if (this.mainTab === 'plugins') return this.t('kicker.plugins');
|
|
152
152
|
if (this.mainTab === 'docs') return this.t('kicker.docs');
|
|
153
153
|
if (this.mainTab === 'trash') return this.t('kicker.trash');
|
|
154
|
+
if (this.mainTab === 'prompts') return this.t('kicker.prompts');
|
|
154
155
|
return this.t('kicker.settings');
|
|
155
156
|
},
|
|
156
157
|
mainTabTitle() {
|
|
@@ -163,6 +164,7 @@ export function createMainTabsComputed() {
|
|
|
163
164
|
if (this.mainTab === 'plugins') return this.t('title.plugins');
|
|
164
165
|
if (this.mainTab === 'docs') return this.t('title.docs');
|
|
165
166
|
if (this.mainTab === 'trash') return this.t('settings.trash.title');
|
|
167
|
+
if (this.mainTab === 'prompts') return this.t('title.prompts');
|
|
166
168
|
return this.t('title.settings');
|
|
167
169
|
},
|
|
168
170
|
mainTabSubtitle() {
|
|
@@ -175,6 +177,7 @@ export function createMainTabsComputed() {
|
|
|
175
177
|
if (this.mainTab === 'plugins') return this.t('subtitle.plugins');
|
|
176
178
|
if (this.mainTab === 'docs') return this.t('subtitle.docs');
|
|
177
179
|
if (this.mainTab === 'trash') return this.t('settings.trash.meta');
|
|
180
|
+
if (this.mainTab === 'prompts') return this.t('subtitle.prompts');
|
|
178
181
|
return this.t('subtitle.settings');
|
|
179
182
|
},
|
|
180
183
|
taskOrchestrationSelectedRun() {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function createPromptsComputed() {
|
|
2
|
+
return {
|
|
3
|
+
promptsContextHint() {
|
|
4
|
+
if (!this.agentsLoading && !this.agentsDiffVisible && this.hasAgentsContentChanged()) {
|
|
5
|
+
return { text: this.t('modal.agents.unsaved.detectedHint'), warn: true };
|
|
6
|
+
}
|
|
7
|
+
if (this.agentsDiffVisible && (this.agentsDiffLoading || this.agentsSaving)) {
|
|
8
|
+
return { text: this.t('diff.hint.busy'), warn: false };
|
|
9
|
+
}
|
|
10
|
+
if (this.agentsDiffVisible && this.agentsDiffError) {
|
|
11
|
+
return { text: this.t('diff.hint.failedBack'), warn: false };
|
|
12
|
+
}
|
|
13
|
+
if (this.agentsDiffVisible && !this.agentsDiffHasChanges) {
|
|
14
|
+
return { text: this.t('diff.hint.noChangesBack'), warn: false };
|
|
15
|
+
}
|
|
16
|
+
if (this.agentsDiffVisible && this.agentsDiffTruncated) {
|
|
17
|
+
return { text: this.t('diff.viewHint.truncated'), warn: false };
|
|
18
|
+
}
|
|
19
|
+
if (this.agentsDiffVisible) {
|
|
20
|
+
return { text: this.t('diff.viewHint.preview'), warn: false };
|
|
21
|
+
}
|
|
22
|
+
if (!this.agentsLoading) {
|
|
23
|
+
return { text: this.t('modal.agents.hint.twoStepSave'), warn: false };
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -777,7 +777,7 @@ export function createSessionComputed() {
|
|
|
777
777
|
? this.sessionUsageDaily
|
|
778
778
|
: null;
|
|
779
779
|
if (!daily || !Array.isArray(daily.rows) || daily.rows.length === 0) {
|
|
780
|
-
return { points: [], labels: [], linePath: '', areaPath: '', width: 800, maxTokens: 0 };
|
|
780
|
+
return { points: [], labels: [], linePath: '', areaPath: '', width: 800, maxTokens: 0, yTicks: [] };
|
|
781
781
|
}
|
|
782
782
|
|
|
783
783
|
const rows = daily.rows;
|
|
@@ -806,6 +806,27 @@ export function createSessionComputed() {
|
|
|
806
806
|
const selectedKey = this.sessionsUsageSelectedDayKey;
|
|
807
807
|
const selectedPoint = points.find(p => p.key === selectedKey) || points[points.length - 1] || null;
|
|
808
808
|
|
|
809
|
+
const yTicks = [];
|
|
810
|
+
if (maxTokens > 0) {
|
|
811
|
+
const rawStep = maxTokens / 4;
|
|
812
|
+
const magnitude = Math.pow(10, Math.floor(Math.log10(rawStep)));
|
|
813
|
+
const residual = rawStep / magnitude;
|
|
814
|
+
const niceStep = residual <= 1.5 ? magnitude : (residual <= 3.5 ? 2 * magnitude : 5 * magnitude);
|
|
815
|
+
const tickCount = Math.min(Math.ceil(maxTokens / niceStep), 8);
|
|
816
|
+
for (let i = 0; i <= tickCount; i++) {
|
|
817
|
+
const value = i * niceStep;
|
|
818
|
+
if (value > maxTokens + niceStep * 0.5) break;
|
|
819
|
+
const normalized = value / maxTokens;
|
|
820
|
+
const y = padding.top + chartHeight - (normalized * chartHeight);
|
|
821
|
+
yTicks.push({
|
|
822
|
+
value,
|
|
823
|
+
label: formatCompactUsageSummaryNumber(value),
|
|
824
|
+
y,
|
|
825
|
+
percent: (((height - y) / height) * 100).toFixed(1)
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
809
830
|
return {
|
|
810
831
|
points,
|
|
811
832
|
labels: rows.map((row, index) => ({
|
|
@@ -817,6 +838,7 @@ export function createSessionComputed() {
|
|
|
817
838
|
width,
|
|
818
839
|
height,
|
|
819
840
|
maxTokens,
|
|
841
|
+
yTicks,
|
|
820
842
|
hoverX: selectedPoint ? selectedPoint.x : 0,
|
|
821
843
|
hoverY: selectedPoint ? selectedPoint.y : 0
|
|
822
844
|
};
|
|
@@ -635,17 +635,63 @@ export function createAgentsMethods(options = {}) {
|
|
|
635
635
|
return;
|
|
636
636
|
}
|
|
637
637
|
const successLabel = this.agentsContext === 'openclaw-workspace'
|
|
638
|
-
?
|
|
638
|
+
? this.t('toast.agents.saved.workspace', { name: this.agentsWorkspaceFileName || '' }).replace(/:\s*$/, '')
|
|
639
639
|
: (this.agentsContext === 'claude-md'
|
|
640
|
-
? '
|
|
641
|
-
: (this.agentsContext === 'openclaw' ? '
|
|
640
|
+
? this.t('toast.agents.saved.claudeMd')
|
|
641
|
+
: (this.agentsContext === 'openclaw' ? this.t('toast.agents.saved.openclaw') : this.t('toast.agents.saved.agents')));
|
|
642
642
|
this.showMessage(successLabel, 'success');
|
|
643
|
-
this.
|
|
643
|
+
if (this.mainTab === 'prompts') {
|
|
644
|
+
this.loadPromptsContent();
|
|
645
|
+
} else {
|
|
646
|
+
this.closeAgentsModal({ force: true });
|
|
647
|
+
}
|
|
644
648
|
} catch (e) {
|
|
645
649
|
this.showMessage(this.t('toast.save.fail'), 'error');
|
|
646
650
|
} finally {
|
|
647
651
|
this.agentsSaving = false;
|
|
648
652
|
}
|
|
653
|
+
},
|
|
654
|
+
|
|
655
|
+
switchPromptsSubTab(subTab) {
|
|
656
|
+
const normalized = subTab === 'claude-md' ? 'claude-md' : 'codex';
|
|
657
|
+
if (this.promptsSubTab === normalized) {
|
|
658
|
+
this.loadPromptsContent();
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
this.promptsSubTab = normalized;
|
|
662
|
+
},
|
|
663
|
+
|
|
664
|
+
async loadPromptsContent() {
|
|
665
|
+
const requestToken = issueLatestRequestToken(this, '_agentsOpenRequestToken');
|
|
666
|
+
this.agentsLoading = true;
|
|
667
|
+
this.resetAgentsDiffState();
|
|
668
|
+
try {
|
|
669
|
+
const isClaude = this.promptsSubTab === 'claude-md';
|
|
670
|
+
const action = isClaude ? 'get-claude-md-file' : 'get-agents-file';
|
|
671
|
+
const res = await api(action);
|
|
672
|
+
if (!isLatestRequestToken(this, '_agentsOpenRequestToken', requestToken)) {
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
if (res.error) {
|
|
676
|
+
this.showMessage(res.error, 'error');
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
this.agentsContent = res.content || '';
|
|
680
|
+
this.agentsOriginalContent = this.agentsContent;
|
|
681
|
+
this.agentsPath = res.path || '';
|
|
682
|
+
this.agentsExists = !!res.exists;
|
|
683
|
+
this.agentsLineEnding = res.lineEnding === '\r\n' ? '\r\n' : '\n';
|
|
684
|
+
this.agentsContext = isClaude ? 'claude-md' : 'codex';
|
|
685
|
+
} catch (e) {
|
|
686
|
+
if (!isLatestRequestToken(this, '_agentsOpenRequestToken', requestToken)) {
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
this.showMessage(this.t('toast.load.fail'), 'error');
|
|
690
|
+
} finally {
|
|
691
|
+
if (isLatestRequestToken(this, '_agentsOpenRequestToken', requestToken)) {
|
|
692
|
+
this.agentsLoading = false;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
649
695
|
}
|
|
650
696
|
};
|
|
651
697
|
}
|
|
@@ -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') {
|
|
@@ -90,6 +90,10 @@ export function createSessionActionMethods(options = {}) {
|
|
|
90
90
|
this.loadSessionStandalonePlain();
|
|
91
91
|
},
|
|
92
92
|
|
|
93
|
+
canBuildStandaloneUrl(session) {
|
|
94
|
+
return !!this.buildSessionStandaloneUrl(session);
|
|
95
|
+
},
|
|
96
|
+
|
|
93
97
|
buildSessionStandaloneUrl(session) {
|
|
94
98
|
if (!session) return '';
|
|
95
99
|
const source = typeof session.source === 'string' ? session.source.trim().toLowerCase() : '';
|
|
@@ -129,6 +133,12 @@ export function createSessionActionMethods(options = {}) {
|
|
|
129
133
|
this.showMessage(this.t('toast.copy.fail'), 'error');
|
|
130
134
|
},
|
|
131
135
|
|
|
136
|
+
openSessionLink(session) {
|
|
137
|
+
const url = this.buildSessionStandaloneUrl(session);
|
|
138
|
+
if (!url) { this.showMessage(this.t('toast.link.fail'), 'error'); return; }
|
|
139
|
+
window.open(url, '_blank', 'noopener,noreferrer');
|
|
140
|
+
},
|
|
141
|
+
|
|
132
142
|
getSessionFilePath(session) {
|
|
133
143
|
const filePath = typeof session?.filePath === 'string' ? session.filePath.trim() : '';
|
|
134
144
|
return filePath;
|
|
@@ -412,11 +422,16 @@ export function createSessionActionMethods(options = {}) {
|
|
|
412
422
|
const model = typeof payload.model === 'string' && payload.model.trim()
|
|
413
423
|
? payload.model.trim()
|
|
414
424
|
: 'glm-4.7';
|
|
415
|
-
|
|
425
|
+
const targetApiRaw = typeof payload.targetApi === 'string' ? payload.targetApi.trim().toLowerCase() : '';
|
|
426
|
+
const targetApi = targetApiRaw === 'chat_completions' || targetApiRaw === 'chat-completions' || targetApiRaw === 'chat/completions'
|
|
427
|
+
? 'chat_completions'
|
|
428
|
+
: (targetApiRaw === 'ollama' ? 'ollama' : 'responses');
|
|
429
|
+
if (!baseUrl || (!apiKey && targetApi !== 'ollama')) return '';
|
|
416
430
|
const urlArg = this.quoteShellArg(baseUrl);
|
|
417
431
|
const keyArg = this.quoteShellArg(apiKey);
|
|
418
432
|
const modelArg = this.quoteShellArg(model);
|
|
419
|
-
|
|
433
|
+
const targetArg = targetApi !== 'responses' ? ` --target-api ${this.quoteShellArg(targetApi)}` : '';
|
|
434
|
+
return `${this.getShareCommandPrefixInvocation()} claude ${urlArg} ${keyArg} ${modelArg}${targetArg}`;
|
|
420
435
|
},
|
|
421
436
|
|
|
422
437
|
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;
|