codexmate 0.0.55 → 0.0.56
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.vi.md +2 -0
- package/README.zh.md +2 -0
- package/cli/local-bridge.js +221 -22
- package/cli.js +117 -5
- package/package.json +1 -1
- package/web-ui/app.js +2 -0
- package/web-ui/modules/app.computed.session.mjs +210 -0
- package/web-ui/modules/app.methods.claude-config.mjs +128 -12
- package/web-ui/modules/app.methods.codex-config.mjs +294 -65
- package/web-ui/modules/app.methods.navigation.mjs +4 -1
- package/web-ui/modules/app.methods.providers.mjs +15 -1
- package/web-ui/modules/app.methods.runtime.mjs +7 -2
- package/web-ui/modules/app.methods.session-actions.mjs +24 -0
- package/web-ui/modules/app.methods.startup-claude.mjs +34 -1
- package/web-ui/modules/i18n/locales/en.mjs +41 -2
- package/web-ui/modules/i18n/locales/ja.mjs +41 -2
- package/web-ui/modules/i18n/locales/vi.mjs +41 -8
- package/web-ui/modules/i18n/locales/zh-tw.mjs +41 -2
- package/web-ui/modules/i18n/locales/zh.mjs +41 -2
- package/web-ui/modules/provider-default-names.mjs +25 -0
- package/web-ui/partials/index/modal-health-check.html +69 -5
- package/web-ui/partials/index/panel-config-codex.html +2 -2
- package/web-ui/partials/index/panel-sessions.html +97 -17
- package/web-ui/res/web-ui-render.precompiled.js +345 -93
- package/web-ui/session-helpers.mjs +4 -1
- package/web-ui/styles/responsive.css +98 -0
- package/web-ui/styles/sessions-preview.css +212 -2
- package/web-ui/styles/sessions-toolbar-trash.css +61 -18
- package/web-ui/styles/skills-list.css +122 -0
- package/web-ui/styles/titles-cards.css +52 -0
|
@@ -51,6 +51,190 @@ function formatUsageRangeLabel(range, t) {
|
|
|
51
51
|
return '近 7 天';
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
function normalizeWorkspaceText(value) {
|
|
55
|
+
if (value == null) {
|
|
56
|
+
return '';
|
|
57
|
+
}
|
|
58
|
+
return String(value)
|
|
59
|
+
.replace(/<[^>]*>/g, ' ')
|
|
60
|
+
.replace(/ /gi, ' ')
|
|
61
|
+
.replace(/</gi, '<')
|
|
62
|
+
.replace(/>/gi, '>')
|
|
63
|
+
.replace(/&/gi, '&')
|
|
64
|
+
.replace(/\s+/g, ' ')
|
|
65
|
+
.trim();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function trimWorkspaceLine(value, maxLength = 160) {
|
|
69
|
+
const text = normalizeWorkspaceText(value);
|
|
70
|
+
if (!text) return '';
|
|
71
|
+
const limit = Number.isFinite(Number(maxLength)) ? Math.max(20, Math.floor(Number(maxLength))) : 160;
|
|
72
|
+
return text.length > limit ? `${text.slice(0, Math.max(0, limit - 1)).trimEnd()}…` : text;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function pushUniqueWorkspaceItem(items, value, limit = 6, maxLength = 160) {
|
|
76
|
+
if (!Array.isArray(items) || items.length >= limit) return;
|
|
77
|
+
const text = trimWorkspaceLine(value, maxLength);
|
|
78
|
+
if (!text) return;
|
|
79
|
+
const key = text.toLowerCase();
|
|
80
|
+
if (items.some(item => String(item || '').toLowerCase() === key)) return;
|
|
81
|
+
items.push(text);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function extractWorkspaceLines(text) {
|
|
85
|
+
return String(text || '')
|
|
86
|
+
.split(/\r?\n|(?:^|\s)(?:[-*•]|\d+[.)])\s+|[。;;]\s*/g)
|
|
87
|
+
.map(line => normalizeWorkspaceText(line))
|
|
88
|
+
.filter(Boolean);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function isWorkspaceCommandLine(line) {
|
|
92
|
+
return /^\$\s+/.test(line)
|
|
93
|
+
|| /^(?:npm|pnpm|yarn|bun|node|npx|git|gh|codex|claude|gemini|codebuddy|openclaw|pytest|go test|cargo test|make|docker)\b/i.test(line);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function extractInlineWorkspaceCommands(text) {
|
|
97
|
+
const source = normalizeWorkspaceText(text);
|
|
98
|
+
if (!source) return [];
|
|
99
|
+
const matches = source.match(/(?:^|[^A-Za-z0-9_])(?:\$\s*)?(?:(?:npm|pnpm|yarn|bun|node|npx|git|gh|codex|claude|gemini|codebuddy|openclaw|pytest|make|docker)\s+(?:run\s+)?[A-Za-z0-9:_./@-]+(?:\s+&&\s+(?:npm|pnpm|yarn|bun|node|npx|git|gh|codex|claude|gemini|codebuddy|openclaw|pytest|make|docker)\s+(?:run\s+)?[A-Za-z0-9:_./@-]+)?|go\s+test(?:\s+[A-Za-z0-9:_./@-]+)?|cargo\s+test(?:\s+[A-Za-z0-9:_./@-]+)?)(?=$|\s|[,。;,;])/gi) || [];
|
|
100
|
+
return matches.map(match => match.replace(/^[^A-Za-z0-9_$]*\$?\s*/, '').trim());
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function extractWorkspaceArtifacts(line, target) {
|
|
104
|
+
if (!target || typeof line !== 'string') return;
|
|
105
|
+
const urlMatches = line.match(/https?:\/\/[^\s)\]"'<>,。;、]+/gi) || [];
|
|
106
|
+
for (const url of urlMatches) {
|
|
107
|
+
pushUniqueWorkspaceItem(target.links, url.replace(/[,。;、.,;:!?]+$/g, ''), 5, 140);
|
|
108
|
+
}
|
|
109
|
+
const fileMatches = line.match(/(?:[A-Za-z]:[\\/]|\/)?(?:[A-Za-z0-9_.@+-]+[\\/])*[A-Za-z0-9_.@+-]+\.(?:mjs|js|cjs|ts|tsx|jsx|vue|css|scss|html|json|jsonl|md|yml|yaml|toml|lock|py|go|rs|sh|txt)/g) || [];
|
|
110
|
+
for (const filePath of fileMatches) {
|
|
111
|
+
pushUniqueWorkspaceItem(target.files, filePath, 8, 120);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function buildWorkspaceBriefText(summary, labels = {}) {
|
|
116
|
+
if (!summary || !summary.available) return '';
|
|
117
|
+
const label = (key, fallback) => (labels && labels[key]) || fallback;
|
|
118
|
+
const lines = [
|
|
119
|
+
`# ${label('title', 'Session workspace brief')}`,
|
|
120
|
+
'',
|
|
121
|
+
`${label('source', 'Source')}: ${summary.sourceLabel || summary.source || '-'}`,
|
|
122
|
+
`${label('messages', 'Messages')}: ${summary.messageCount}`,
|
|
123
|
+
`${label('path', 'Path')}: ${summary.cwd || '-'}`,
|
|
124
|
+
''
|
|
125
|
+
];
|
|
126
|
+
const sections = [
|
|
127
|
+
['signals', label('signals', 'Signals'), summary.signals],
|
|
128
|
+
['commands', label('commands', 'Reusable commands'), summary.commands],
|
|
129
|
+
['files', label('files', 'Files'), summary.files],
|
|
130
|
+
['links', label('links', 'Links'), summary.links],
|
|
131
|
+
['risks', label('risks', 'Risks / blockers'), summary.risks],
|
|
132
|
+
['nextSteps', label('nextSteps', 'Next steps'), summary.nextSteps]
|
|
133
|
+
];
|
|
134
|
+
for (const [, sectionTitle, items] of sections) {
|
|
135
|
+
if (!Array.isArray(items) || !items.length) continue;
|
|
136
|
+
lines.push(`## ${sectionTitle}`);
|
|
137
|
+
for (const item of items) {
|
|
138
|
+
lines.push(`- ${item}`);
|
|
139
|
+
}
|
|
140
|
+
lines.push('');
|
|
141
|
+
}
|
|
142
|
+
return lines.join('\n').trimEnd();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function buildSessionWorkspaceSummary(session, messages, options = {}) {
|
|
146
|
+
const safeMessages = Array.isArray(messages) ? messages : [];
|
|
147
|
+
const roleCounts = { user: 0, assistant: 0, system: 0, other: 0 };
|
|
148
|
+
const signals = [];
|
|
149
|
+
const commands = [];
|
|
150
|
+
const files = [];
|
|
151
|
+
const links = [];
|
|
152
|
+
const risks = [];
|
|
153
|
+
const nextSteps = [];
|
|
154
|
+
const target = { files, links };
|
|
155
|
+
let firstUser = '';
|
|
156
|
+
let lastUser = '';
|
|
157
|
+
let lastAssistant = '';
|
|
158
|
+
let firstTimestamp = '';
|
|
159
|
+
let lastTimestamp = '';
|
|
160
|
+
|
|
161
|
+
safeMessages.forEach((message) => {
|
|
162
|
+
if (!message || typeof message !== 'object') return;
|
|
163
|
+
const normalizedRole = String(message.normalizedRole || message.role || '').trim().toLowerCase();
|
|
164
|
+
const role = normalizedRole === 'user' || normalizedRole === 'assistant' || normalizedRole === 'system'
|
|
165
|
+
? normalizedRole
|
|
166
|
+
: 'other';
|
|
167
|
+
roleCounts[role] += 1;
|
|
168
|
+
const timestamp = normalizeWorkspaceText(message.timestamp || '');
|
|
169
|
+
if (timestamp && !firstTimestamp) firstTimestamp = timestamp;
|
|
170
|
+
if (timestamp) lastTimestamp = timestamp;
|
|
171
|
+
const text = normalizeWorkspaceText(message.text || message.content || '');
|
|
172
|
+
if (!text) return;
|
|
173
|
+
if (role === 'user') {
|
|
174
|
+
if (!firstUser) firstUser = text;
|
|
175
|
+
lastUser = text;
|
|
176
|
+
} else if (role === 'assistant') {
|
|
177
|
+
lastAssistant = text;
|
|
178
|
+
}
|
|
179
|
+
const lines = extractWorkspaceLines(message.text || message.content || text);
|
|
180
|
+
for (const command of extractInlineWorkspaceCommands(message.text || message.content || text)) {
|
|
181
|
+
pushUniqueWorkspaceItem(commands, command, 6, 140);
|
|
182
|
+
}
|
|
183
|
+
for (const line of lines) {
|
|
184
|
+
extractWorkspaceArtifacts(line, target);
|
|
185
|
+
if (isWorkspaceCommandLine(line)) {
|
|
186
|
+
pushUniqueWorkspaceItem(commands, line.replace(/^\$\s+/, ''), 6, 140);
|
|
187
|
+
}
|
|
188
|
+
if (/(?:blocker|blocked|risk|failed|failure|error|timeout|regression|冲突|失败|错误|阻塞|风险|不可合|超时|回归)/i.test(line)) {
|
|
189
|
+
pushUniqueWorkspaceItem(risks, line, 5, 150);
|
|
190
|
+
}
|
|
191
|
+
if (/(?:todo|next|follow[- ]?up|remaining|后续|下一步|待办|继续|剩余)/i.test(line)) {
|
|
192
|
+
pushUniqueWorkspaceItem(nextSteps, line, 5, 150);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
pushUniqueWorkspaceItem(signals, firstUser, 3, 150);
|
|
198
|
+
if (lastUser && lastUser !== firstUser) {
|
|
199
|
+
pushUniqueWorkspaceItem(signals, lastUser, 3, 150);
|
|
200
|
+
}
|
|
201
|
+
pushUniqueWorkspaceItem(signals, lastAssistant, 3, 150);
|
|
202
|
+
|
|
203
|
+
const source = session && typeof session.source === 'string' ? session.source : '';
|
|
204
|
+
const sourceLabel = session && typeof session.sourceLabel === 'string' ? session.sourceLabel : source;
|
|
205
|
+
const cwd = session && typeof session.cwd === 'string' ? session.cwd : '';
|
|
206
|
+
const messageCount = safeMessages.length;
|
|
207
|
+
const available = messageCount > 0;
|
|
208
|
+
const metrics = [
|
|
209
|
+
{ key: 'messages', value: String(messageCount), label: options.messagesLabel || 'Messages' },
|
|
210
|
+
{ key: 'user', value: String(roleCounts.user), label: options.userLabel || 'User' },
|
|
211
|
+
{ key: 'assistant', value: String(roleCounts.assistant), label: options.assistantLabel || 'Assistant' },
|
|
212
|
+
{ key: 'commands', value: String(commands.length), label: options.commandsLabel || 'Commands' },
|
|
213
|
+
{ key: 'artifacts', value: String(files.length + links.length), label: options.artifactsLabel || 'Artifacts' },
|
|
214
|
+
{ key: 'risks', value: String(risks.length + nextSteps.length), label: options.risksLabel || 'Risks' }
|
|
215
|
+
];
|
|
216
|
+
|
|
217
|
+
const summary = {
|
|
218
|
+
available,
|
|
219
|
+
source,
|
|
220
|
+
sourceLabel,
|
|
221
|
+
cwd,
|
|
222
|
+
firstTimestamp,
|
|
223
|
+
lastTimestamp,
|
|
224
|
+
messageCount,
|
|
225
|
+
roleCounts,
|
|
226
|
+
metrics,
|
|
227
|
+
signals,
|
|
228
|
+
commands,
|
|
229
|
+
files,
|
|
230
|
+
links,
|
|
231
|
+
risks,
|
|
232
|
+
nextSteps
|
|
233
|
+
};
|
|
234
|
+
summary.briefText = buildWorkspaceBriefText(summary, options.briefLabels || {});
|
|
235
|
+
return summary;
|
|
236
|
+
}
|
|
237
|
+
|
|
54
238
|
function formatUsageDuration(value, options = {}) {
|
|
55
239
|
const normalizedLang = typeof options.lang === 'string' ? options.lang.trim().toLowerCase() : '';
|
|
56
240
|
const isEn = normalizedLang === 'en';
|
|
@@ -267,6 +451,32 @@ export function createSessionComputed() {
|
|
|
267
451
|
if (index < 0) return 0;
|
|
268
452
|
return Math.round(((index + 1) / this.sessionTimelineNodes.length) * 100);
|
|
269
453
|
},
|
|
454
|
+
activeSessionWorkspaceSummary() {
|
|
455
|
+
if (this.mainTab !== 'sessions' || !this.activeSession) {
|
|
456
|
+
return buildSessionWorkspaceSummary(null, []);
|
|
457
|
+
}
|
|
458
|
+
const t = typeof this.t === 'function' ? this.t : null;
|
|
459
|
+
return buildSessionWorkspaceSummary(this.activeSession, this.activeSessionMessages, {
|
|
460
|
+
messagesLabel: t ? t('sessions.workspace.metric.messages') : 'Messages',
|
|
461
|
+
userLabel: t ? t('sessions.workspace.metric.user') : 'User',
|
|
462
|
+
assistantLabel: t ? t('sessions.workspace.metric.assistant') : 'Assistant',
|
|
463
|
+
commandsLabel: t ? t('sessions.workspace.metric.commands') : 'Commands',
|
|
464
|
+
artifactsLabel: t ? t('sessions.workspace.metric.artifacts') : 'Artifacts',
|
|
465
|
+
risksLabel: t ? t('sessions.workspace.metric.risks') : 'Risks',
|
|
466
|
+
briefLabels: {
|
|
467
|
+
title: t ? t('sessions.workspace.copy.title') : 'Session workspace brief',
|
|
468
|
+
source: t ? t('sessions.workspace.copy.source') : 'Source',
|
|
469
|
+
messages: t ? t('sessions.workspace.metric.messages') : 'Messages',
|
|
470
|
+
path: t ? t('sessions.workspace.copy.path') : 'Path',
|
|
471
|
+
signals: t ? t('sessions.workspace.signals') : 'Signals',
|
|
472
|
+
commands: t ? t('sessions.workspace.commands') : 'Reusable commands',
|
|
473
|
+
files: t ? t('sessions.workspace.files') : 'Files',
|
|
474
|
+
links: t ? t('sessions.workspace.links') : 'Links',
|
|
475
|
+
risks: t ? t('sessions.workspace.risks') : 'Risks / blockers',
|
|
476
|
+
nextSteps: t ? t('sessions.workspace.nextSteps') : 'Next steps'
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
},
|
|
270
480
|
sessionQueryPlaceholder() {
|
|
271
481
|
if (this.isSessionQueryEnabled) {
|
|
272
482
|
return typeof this.t === 'function'
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { nextClaudeConfigName } from './provider-default-names.mjs';
|
|
2
|
+
|
|
1
3
|
function normalizeClaudeText(value) {
|
|
2
4
|
return typeof value === 'string' ? value.trim() : '';
|
|
3
5
|
}
|
|
@@ -6,6 +8,21 @@ function normalizeClaudeBaseUrl(value) {
|
|
|
6
8
|
return normalizeClaudeText(value).replace(/\/+$/g, '');
|
|
7
9
|
}
|
|
8
10
|
|
|
11
|
+
const DELETED_CLAUDE_SETTINGS_IMPORTS_KEY = 'deletedClaudeSettingsImports';
|
|
12
|
+
|
|
13
|
+
function buildDeletedClaudeSettingsFingerprint(config = {}) {
|
|
14
|
+
const safe = config && typeof config === 'object' ? config : {};
|
|
15
|
+
const baseUrl = normalizeClaudeBaseUrl(safe.baseUrl);
|
|
16
|
+
const model = normalizeClaudeText(safe.model);
|
|
17
|
+
if (!baseUrl || !model) return null;
|
|
18
|
+
return {
|
|
19
|
+
baseUrl,
|
|
20
|
+
model,
|
|
21
|
+
providerCacheRef: normalizeClaudeText(safe.providerCacheRef),
|
|
22
|
+
deletedAt: Date.now()
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
9
26
|
function isValidClaudeHttpUrl(value) {
|
|
10
27
|
if (!value) return false;
|
|
11
28
|
try {
|
|
@@ -114,6 +131,46 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
114
131
|
this.syncClaudeBridgeProviders();
|
|
115
132
|
},
|
|
116
133
|
|
|
134
|
+
rememberDeletedClaudeSettingsImport(config) {
|
|
135
|
+
const fingerprint = buildDeletedClaudeSettingsFingerprint(config);
|
|
136
|
+
if (!fingerprint) return;
|
|
137
|
+
try {
|
|
138
|
+
const raw = localStorage.getItem(DELETED_CLAUDE_SETTINGS_IMPORTS_KEY);
|
|
139
|
+
const parsed = raw ? JSON.parse(raw) : [];
|
|
140
|
+
const entries = Array.isArray(parsed) ? parsed : [];
|
|
141
|
+
const deduped = entries.filter((entry) => {
|
|
142
|
+
if (!entry || typeof entry !== 'object') return false;
|
|
143
|
+
return normalizeClaudeBaseUrl(entry.baseUrl) !== fingerprint.baseUrl
|
|
144
|
+
|| normalizeClaudeText(entry.model) !== fingerprint.model;
|
|
145
|
+
});
|
|
146
|
+
deduped.push(fingerprint);
|
|
147
|
+
localStorage.setItem(DELETED_CLAUDE_SETTINGS_IMPORTS_KEY, JSON.stringify(deduped.slice(-50)));
|
|
148
|
+
} catch (_) {}
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
async applyCurrentClaudeConfigSilently() {
|
|
152
|
+
const name = normalizeClaudeText(this.currentClaudeConfig);
|
|
153
|
+
if (!name || !this.claudeConfigs || !this.claudeConfigs[name]) return false;
|
|
154
|
+
if (typeof this.applyClaudeConfig !== 'function') return false;
|
|
155
|
+
return await this.applyClaudeConfig(name, { silent: true });
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
selectClaudeFallbackConfigName(excludedNames = []) {
|
|
159
|
+
const configs = this.claudeConfigs && typeof this.claudeConfigs === 'object' ? this.claudeConfigs : {};
|
|
160
|
+
const excluded = new Set((Array.isArray(excludedNames) ? excludedNames : [excludedNames])
|
|
161
|
+
.map((name) => normalizeClaudeText(name))
|
|
162
|
+
.filter(Boolean));
|
|
163
|
+
const names = Object.keys(configs).filter((name) => name && !excluded.has(name));
|
|
164
|
+
const applyable = names.find((name) => {
|
|
165
|
+
const config = configs[name] || {};
|
|
166
|
+
return !!(config.apiKey
|
|
167
|
+
|| config.providerCacheRef
|
|
168
|
+
|| config.externalCredentialType
|
|
169
|
+
|| config.targetApi === 'ollama');
|
|
170
|
+
});
|
|
171
|
+
return applyable || names[0] || '';
|
|
172
|
+
},
|
|
173
|
+
|
|
117
174
|
async hydrateClaudeConfigsFromProviderCache(options = {}) {
|
|
118
175
|
const silent = options && options.silent === true;
|
|
119
176
|
try {
|
|
@@ -123,10 +180,10 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
123
180
|
return false;
|
|
124
181
|
}
|
|
125
182
|
const providers = Array.isArray(res.providers) ? res.providers : [];
|
|
126
|
-
if (providers.length === 0) return true;
|
|
127
183
|
const configs = this.claudeConfigs && typeof this.claudeConfigs === 'object' ? this.claudeConfigs : {};
|
|
128
184
|
let changed = false;
|
|
129
185
|
let firstCachedName = '';
|
|
186
|
+
const liveCacheRefs = new Set();
|
|
130
187
|
for (const provider of providers) {
|
|
131
188
|
if (!provider || typeof provider !== 'object') continue;
|
|
132
189
|
const name = normalizeClaudeText(provider.name);
|
|
@@ -134,12 +191,15 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
134
191
|
const model = normalizeClaudeText(provider.model);
|
|
135
192
|
if (!name || !baseUrl || !model) continue;
|
|
136
193
|
if (!firstCachedName) firstCachedName = name;
|
|
194
|
+
const providerCacheRef = normalizeClaudeText(provider.providerCacheRef) || name;
|
|
195
|
+
liveCacheRefs.add(name);
|
|
196
|
+
liveCacheRefs.add(providerCacheRef);
|
|
137
197
|
const cachedConfig = {
|
|
138
198
|
apiKey: '',
|
|
139
199
|
baseUrl,
|
|
140
200
|
model,
|
|
141
201
|
hasKey: provider.hasKey === true,
|
|
142
|
-
providerCacheRef
|
|
202
|
+
providerCacheRef,
|
|
143
203
|
source: 'provider-cache',
|
|
144
204
|
targetApi: normalizeClaudeText(provider.targetApi) || 'responses'
|
|
145
205
|
};
|
|
@@ -152,11 +212,26 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
152
212
|
changed = true;
|
|
153
213
|
}
|
|
154
214
|
}
|
|
215
|
+
for (const [name, existing] of Object.entries(configs)) {
|
|
216
|
+
if (!existing || typeof existing !== 'object') continue;
|
|
217
|
+
const providerCacheRef = normalizeClaudeText(existing.providerCacheRef);
|
|
218
|
+
const source = normalizeClaudeText(existing.source);
|
|
219
|
+
const cacheBacked = source === 'provider-cache' || !!providerCacheRef;
|
|
220
|
+
if (!cacheBacked) continue;
|
|
221
|
+
const ref = providerCacheRef || normalizeClaudeText(name);
|
|
222
|
+
if (liveCacheRefs.has(ref) || liveCacheRefs.has(normalizeClaudeText(name))) continue;
|
|
223
|
+
delete configs[name];
|
|
224
|
+
changed = true;
|
|
225
|
+
}
|
|
155
226
|
this.claudeConfigs = configs;
|
|
156
|
-
|
|
227
|
+
const current = normalizeClaudeText(this.currentClaudeConfig);
|
|
228
|
+
if (current && !configs[current]) {
|
|
229
|
+
this.currentClaudeConfig = firstCachedName || Object.keys(configs)[0] || '';
|
|
230
|
+
try { localStorage.setItem('currentClaudeConfig', this.currentClaudeConfig); } catch (_) {}
|
|
231
|
+
changed = true;
|
|
232
|
+
} else if (firstCachedName) {
|
|
157
233
|
let savedCurrent = '';
|
|
158
234
|
try { savedCurrent = localStorage.getItem('currentClaudeConfig') || ''; } catch (_) {}
|
|
159
|
-
const current = normalizeClaudeText(this.currentClaudeConfig);
|
|
160
235
|
const currentConfig = current && configs[current] ? configs[current] : null;
|
|
161
236
|
if (!savedCurrent && (!current || (currentConfig && currentConfig.hasKey === false && !currentConfig.providerCacheRef))) {
|
|
162
237
|
this.currentClaudeConfig = firstCachedName;
|
|
@@ -323,6 +398,28 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
323
398
|
await this.applyClaudeConfig(name);
|
|
324
399
|
},
|
|
325
400
|
|
|
401
|
+
async deleteClaudeProviderCacheRef(configOrName) {
|
|
402
|
+
const config = configOrName && typeof configOrName === 'object'
|
|
403
|
+
? configOrName
|
|
404
|
+
: (this.claudeConfigs && this.claudeConfigs[configOrName] ? this.claudeConfigs[configOrName] : null);
|
|
405
|
+
const ref = normalizeClaudeText(config && config.providerCacheRef);
|
|
406
|
+
const source = normalizeClaudeText(config && config.source);
|
|
407
|
+
if (!ref && source !== 'provider-cache') return true;
|
|
408
|
+
const name = ref || normalizeClaudeText(config && config.name) || normalizeClaudeText(configOrName);
|
|
409
|
+
if (!name) return true;
|
|
410
|
+
try {
|
|
411
|
+
const res = await api('delete-provider-cache-record', { name, group: 'claude' });
|
|
412
|
+
if (res && res.error) {
|
|
413
|
+
this.showMessage(res.error, 'error');
|
|
414
|
+
return false;
|
|
415
|
+
}
|
|
416
|
+
return true;
|
|
417
|
+
} catch (e) {
|
|
418
|
+
this.showMessage(e && e.message ? e.message : this.t('toast.operation.fail'), 'error');
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
},
|
|
422
|
+
|
|
326
423
|
async deleteClaudeConfig(name) {
|
|
327
424
|
if (Object.keys(this.claudeConfigs).length <= 1) {
|
|
328
425
|
return this.showMessage(this.t('toast.claude.keepOne'), 'error');
|
|
@@ -336,16 +433,30 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
336
433
|
});
|
|
337
434
|
if (!confirmed) return;
|
|
338
435
|
|
|
436
|
+
const config = this.claudeConfigs[name];
|
|
437
|
+
const cacheDeleted = await this.deleteClaudeProviderCacheRef(config || name);
|
|
438
|
+
if (!cacheDeleted) return;
|
|
439
|
+
|
|
440
|
+
if (typeof this.rememberDeletedClaudeSettingsImport === 'function') {
|
|
441
|
+
this.rememberDeletedClaudeSettingsImport(config);
|
|
442
|
+
}
|
|
339
443
|
delete this.claudeConfigs[name];
|
|
340
444
|
if (this.currentClaudeConfig === name) {
|
|
341
|
-
this.currentClaudeConfig =
|
|
445
|
+
this.currentClaudeConfig = this.selectClaudeFallbackConfigName();
|
|
342
446
|
}
|
|
343
447
|
this.saveClaudeConfigs();
|
|
344
448
|
this.showMessage(this.t('toast.operation.success'), 'success');
|
|
345
|
-
this.
|
|
449
|
+
if (this.currentClaudeConfig) {
|
|
450
|
+
await this.applyCurrentClaudeConfigSilently();
|
|
451
|
+
} else {
|
|
452
|
+
this.refreshClaudeModelContext();
|
|
453
|
+
}
|
|
346
454
|
},
|
|
347
455
|
|
|
348
|
-
async applyClaudeConfig(name) {
|
|
456
|
+
async applyClaudeConfig(name, options = {}) {
|
|
457
|
+
const silent = !!(options && options.silent);
|
|
458
|
+
const silentSuccess = silent || !!(options && options.silentSuccess);
|
|
459
|
+
const silentError = silent || !!(options && options.silentError);
|
|
349
460
|
this.currentClaudeConfig = name;
|
|
350
461
|
try { localStorage.setItem('currentClaudeConfig', name || ''); } catch (_) {}
|
|
351
462
|
this.refreshClaudeModelContext();
|
|
@@ -353,8 +464,10 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
353
464
|
|
|
354
465
|
if (!config.apiKey && !config.providerCacheRef && config.targetApi !== 'ollama') {
|
|
355
466
|
if (config.externalCredentialType) {
|
|
467
|
+
if (silentError) return false;
|
|
356
468
|
return this.showMessage(this.t('toast.claude.externalAuth'), 'info');
|
|
357
469
|
}
|
|
470
|
+
if (silentError) return false;
|
|
358
471
|
return this.showMessage(this.t('toast.claude.apiKeyRequired'), 'error');
|
|
359
472
|
}
|
|
360
473
|
|
|
@@ -362,15 +475,18 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
362
475
|
try {
|
|
363
476
|
const res = await api('apply-claude-config', { config: { ...config, name } });
|
|
364
477
|
if (res.error || res.success === false) {
|
|
365
|
-
this.showMessage(res.error || this.t('toast.apply.fail'), 'error');
|
|
478
|
+
if (!silentError) this.showMessage(res.error || this.t('toast.apply.fail'), 'error');
|
|
479
|
+
return false;
|
|
366
480
|
} else {
|
|
367
|
-
if (this._lastAppliedClaudeKey !== _claudeKey2) {
|
|
481
|
+
if (!silentSuccess && this._lastAppliedClaudeKey !== _claudeKey2) {
|
|
368
482
|
this.showMessage(this.t('toast.apply.success'), 'success');
|
|
369
|
-
this._lastAppliedClaudeKey = _claudeKey2;
|
|
370
483
|
}
|
|
484
|
+
this._lastAppliedClaudeKey = _claudeKey2;
|
|
485
|
+
return true;
|
|
371
486
|
}
|
|
372
487
|
} catch (_) {
|
|
373
|
-
this.showMessage(this.t('toast.apply.fail'), 'error');
|
|
488
|
+
if (!silentError) this.showMessage(this.t('toast.apply.fail'), 'error');
|
|
489
|
+
return false;
|
|
374
490
|
}
|
|
375
491
|
},
|
|
376
492
|
|
|
@@ -378,7 +494,7 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
378
494
|
this.showClaudeConfigModal = false;
|
|
379
495
|
this.showAddClaudeConfigKey = false;
|
|
380
496
|
this.newClaudeConfig = {
|
|
381
|
-
name:
|
|
497
|
+
name: nextClaudeConfigName(this.claudeConfigs),
|
|
382
498
|
apiKey: '',
|
|
383
499
|
externalCredentialType: '',
|
|
384
500
|
baseUrl: '',
|