commons-proxy 2.0.0
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/LICENSE +21 -0
- package/README.md +757 -0
- package/bin/cli.js +146 -0
- package/package.json +97 -0
- package/public/Complaint Details.pdf +0 -0
- package/public/Cyber Crime Portal.pdf +0 -0
- package/public/app.js +229 -0
- package/public/css/src/input.css +523 -0
- package/public/css/style.css +1 -0
- package/public/favicon.png +0 -0
- package/public/index.html +549 -0
- package/public/js/components/account-manager.js +356 -0
- package/public/js/components/add-account-modal.js +414 -0
- package/public/js/components/claude-config.js +420 -0
- package/public/js/components/dashboard/charts.js +605 -0
- package/public/js/components/dashboard/filters.js +362 -0
- package/public/js/components/dashboard/stats.js +110 -0
- package/public/js/components/dashboard.js +236 -0
- package/public/js/components/logs-viewer.js +100 -0
- package/public/js/components/models.js +36 -0
- package/public/js/components/server-config.js +349 -0
- package/public/js/config/constants.js +102 -0
- package/public/js/data-store.js +375 -0
- package/public/js/settings-store.js +58 -0
- package/public/js/store.js +99 -0
- package/public/js/translations/en.js +367 -0
- package/public/js/translations/id.js +412 -0
- package/public/js/translations/pt.js +308 -0
- package/public/js/translations/tr.js +358 -0
- package/public/js/translations/zh.js +373 -0
- package/public/js/utils/account-actions.js +189 -0
- package/public/js/utils/error-handler.js +96 -0
- package/public/js/utils/model-config.js +42 -0
- package/public/js/utils/ui-logger.js +143 -0
- package/public/js/utils/validators.js +77 -0
- package/public/js/utils.js +69 -0
- package/public/proxy-server-64.png +0 -0
- package/public/views/accounts.html +361 -0
- package/public/views/dashboard.html +484 -0
- package/public/views/logs.html +97 -0
- package/public/views/models.html +331 -0
- package/public/views/settings.html +1327 -0
- package/src/account-manager/credentials.js +378 -0
- package/src/account-manager/index.js +462 -0
- package/src/account-manager/onboarding.js +112 -0
- package/src/account-manager/rate-limits.js +369 -0
- package/src/account-manager/storage.js +160 -0
- package/src/account-manager/strategies/base-strategy.js +109 -0
- package/src/account-manager/strategies/hybrid-strategy.js +339 -0
- package/src/account-manager/strategies/index.js +79 -0
- package/src/account-manager/strategies/round-robin-strategy.js +76 -0
- package/src/account-manager/strategies/sticky-strategy.js +138 -0
- package/src/account-manager/strategies/trackers/health-tracker.js +162 -0
- package/src/account-manager/strategies/trackers/index.js +9 -0
- package/src/account-manager/strategies/trackers/quota-tracker.js +120 -0
- package/src/account-manager/strategies/trackers/token-bucket-tracker.js +155 -0
- package/src/auth/database.js +169 -0
- package/src/auth/oauth.js +548 -0
- package/src/auth/token-extractor.js +117 -0
- package/src/cli/accounts.js +648 -0
- package/src/cloudcode/index.js +29 -0
- package/src/cloudcode/message-handler.js +510 -0
- package/src/cloudcode/model-api.js +248 -0
- package/src/cloudcode/rate-limit-parser.js +235 -0
- package/src/cloudcode/request-builder.js +93 -0
- package/src/cloudcode/session-manager.js +47 -0
- package/src/cloudcode/sse-parser.js +121 -0
- package/src/cloudcode/sse-streamer.js +293 -0
- package/src/cloudcode/streaming-handler.js +615 -0
- package/src/config.js +125 -0
- package/src/constants.js +407 -0
- package/src/errors.js +242 -0
- package/src/fallback-config.js +29 -0
- package/src/format/content-converter.js +193 -0
- package/src/format/index.js +20 -0
- package/src/format/request-converter.js +255 -0
- package/src/format/response-converter.js +120 -0
- package/src/format/schema-sanitizer.js +673 -0
- package/src/format/signature-cache.js +88 -0
- package/src/format/thinking-utils.js +648 -0
- package/src/index.js +148 -0
- package/src/modules/usage-stats.js +205 -0
- package/src/providers/anthropic-provider.js +258 -0
- package/src/providers/base-provider.js +157 -0
- package/src/providers/cloudcode.js +94 -0
- package/src/providers/copilot.js +399 -0
- package/src/providers/github-provider.js +287 -0
- package/src/providers/google-provider.js +192 -0
- package/src/providers/index.js +211 -0
- package/src/providers/openai-compatible.js +265 -0
- package/src/providers/openai-provider.js +271 -0
- package/src/providers/openrouter-provider.js +325 -0
- package/src/providers/setup.js +83 -0
- package/src/server.js +870 -0
- package/src/utils/claude-config.js +245 -0
- package/src/utils/helpers.js +51 -0
- package/src/utils/logger.js +142 -0
- package/src/utils/native-module-helper.js +162 -0
- package/src/webui/index.js +1134 -0
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Config Component
|
|
3
|
+
* Registers itself to window.Components for Alpine.js to consume
|
|
4
|
+
*/
|
|
5
|
+
window.Components = window.Components || {};
|
|
6
|
+
|
|
7
|
+
window.Components.claudeConfig = () => ({
|
|
8
|
+
config: { env: {} },
|
|
9
|
+
configPath: '', // Dynamic path from backend
|
|
10
|
+
models: [],
|
|
11
|
+
loading: false,
|
|
12
|
+
restoring: false,
|
|
13
|
+
gemini1mSuffix: false,
|
|
14
|
+
|
|
15
|
+
// Presets state
|
|
16
|
+
presets: [],
|
|
17
|
+
selectedPresetName: '',
|
|
18
|
+
savingPreset: false,
|
|
19
|
+
deletingPreset: false,
|
|
20
|
+
pendingPresetName: '', // For unsaved changes confirmation
|
|
21
|
+
newPresetName: '', // For save preset modal input
|
|
22
|
+
|
|
23
|
+
// Model fields that may contain Gemini model names
|
|
24
|
+
geminiModelFields: [
|
|
25
|
+
'ANTHROPIC_MODEL',
|
|
26
|
+
'CLAUDE_CODE_SUBAGENT_MODEL',
|
|
27
|
+
'ANTHROPIC_DEFAULT_OPUS_MODEL',
|
|
28
|
+
'ANTHROPIC_DEFAULT_SONNET_MODEL',
|
|
29
|
+
'ANTHROPIC_DEFAULT_HAIKU_MODEL'
|
|
30
|
+
],
|
|
31
|
+
|
|
32
|
+
init() {
|
|
33
|
+
// Only fetch config if this is the active sub-tab
|
|
34
|
+
if (this.activeTab === 'claude') {
|
|
35
|
+
this.fetchConfig();
|
|
36
|
+
this.fetchPresets();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Watch local activeTab (from parent settings scope, skip initial trigger)
|
|
40
|
+
this.$watch('activeTab', (tab, oldTab) => {
|
|
41
|
+
if (tab === 'claude' && oldTab !== undefined) {
|
|
42
|
+
this.fetchConfig();
|
|
43
|
+
this.fetchPresets();
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
this.$watch('$store.data.models', (val) => {
|
|
48
|
+
this.models = val || [];
|
|
49
|
+
});
|
|
50
|
+
this.models = Alpine.store('data').models || [];
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Detect if any Gemini model has [1m] suffix
|
|
55
|
+
*/
|
|
56
|
+
detectGemini1mSuffix() {
|
|
57
|
+
for (const field of this.geminiModelFields) {
|
|
58
|
+
const val = this.config.env[field];
|
|
59
|
+
if (val && val.toLowerCase().includes('gemini') && val.includes('[1m]')) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Toggle [1m] suffix for all Gemini models
|
|
68
|
+
*/
|
|
69
|
+
toggleGemini1mSuffix(enabled) {
|
|
70
|
+
for (const field of this.geminiModelFields) {
|
|
71
|
+
const val = this.config.env[field];
|
|
72
|
+
// Fix: Case-insensitive check for gemini
|
|
73
|
+
if (val && /gemini/i.test(val)) {
|
|
74
|
+
if (enabled && !val.includes('[1m]')) {
|
|
75
|
+
this.config.env[field] = val.trim() + '[1m]';
|
|
76
|
+
} else if (!enabled && val.includes('[1m]')) {
|
|
77
|
+
this.config.env[field] = val.replace(/\s*\[1m\]$/i, '').trim();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
this.gemini1mSuffix = enabled;
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Helper to select a model from the dropdown
|
|
86
|
+
* @param {string} field - The config.env field to update
|
|
87
|
+
* @param {string} modelId - The selected model ID
|
|
88
|
+
*/
|
|
89
|
+
selectModel(field, modelId) {
|
|
90
|
+
if (!this.config.env) this.config.env = {};
|
|
91
|
+
|
|
92
|
+
let finalModelId = modelId;
|
|
93
|
+
// If 1M mode is enabled and it's a Gemini model, append the suffix
|
|
94
|
+
if (this.gemini1mSuffix && modelId.toLowerCase().includes('gemini')) {
|
|
95
|
+
if (!finalModelId.includes('[1m]')) {
|
|
96
|
+
finalModelId = finalModelId.trim() + '[1m]';
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
this.config.env[field] = finalModelId;
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
async fetchConfig() {
|
|
104
|
+
const password = Alpine.store('global').webuiPassword;
|
|
105
|
+
try {
|
|
106
|
+
const { response, newPassword } = await window.utils.request('/api/claude/config', {}, password);
|
|
107
|
+
if (newPassword) Alpine.store('global').webuiPassword = newPassword;
|
|
108
|
+
|
|
109
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
110
|
+
const data = await response.json();
|
|
111
|
+
this.config = data.config || {};
|
|
112
|
+
this.configPath = data.path || '~/.claude/settings.json'; // Save dynamic path
|
|
113
|
+
if (!this.config.env) this.config.env = {};
|
|
114
|
+
|
|
115
|
+
// Default MCP CLI to true if not set
|
|
116
|
+
if (this.config.env.ENABLE_EXPERIMENTAL_MCP_CLI === undefined) {
|
|
117
|
+
this.config.env.ENABLE_EXPERIMENTAL_MCP_CLI = 'true';
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Detect existing [1m] suffix state, default to true
|
|
121
|
+
const hasExistingSuffix = this.detectGemini1mSuffix();
|
|
122
|
+
const hasGeminiModels = this.geminiModelFields.some(f =>
|
|
123
|
+
this.config.env[f]?.toLowerCase().includes('gemini')
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
// Default to enabled: if no suffix found but Gemini models exist, apply suffix
|
|
127
|
+
if (!hasExistingSuffix && hasGeminiModels) {
|
|
128
|
+
this.toggleGemini1mSuffix(true);
|
|
129
|
+
} else {
|
|
130
|
+
this.gemini1mSuffix = hasExistingSuffix || !hasGeminiModels;
|
|
131
|
+
}
|
|
132
|
+
} catch (e) {
|
|
133
|
+
console.error('Failed to fetch Claude config:', e);
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
async saveClaudeConfig() {
|
|
138
|
+
this.loading = true;
|
|
139
|
+
const password = Alpine.store('global').webuiPassword;
|
|
140
|
+
try {
|
|
141
|
+
const { response, newPassword } = await window.utils.request('/api/claude/config', {
|
|
142
|
+
method: 'POST',
|
|
143
|
+
headers: { 'Content-Type': 'application/json' },
|
|
144
|
+
body: JSON.stringify(this.config)
|
|
145
|
+
}, password);
|
|
146
|
+
if (newPassword) Alpine.store('global').webuiPassword = newPassword;
|
|
147
|
+
|
|
148
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
149
|
+
Alpine.store('global').showToast(Alpine.store('global').t('claudeConfigSaved'), 'success');
|
|
150
|
+
} catch (e) {
|
|
151
|
+
Alpine.store('global').showToast(Alpine.store('global').t('saveConfigFailed') + ': ' + e.message, 'error');
|
|
152
|
+
} finally {
|
|
153
|
+
this.loading = false;
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
restoreDefaultClaudeConfig() {
|
|
158
|
+
document.getElementById('restore_defaults_modal').showModal();
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
async executeRestore() {
|
|
162
|
+
this.restoring = true;
|
|
163
|
+
const password = Alpine.store('global').webuiPassword;
|
|
164
|
+
try {
|
|
165
|
+
const { response, newPassword } = await window.utils.request('/api/claude/config/restore', {
|
|
166
|
+
method: 'POST',
|
|
167
|
+
headers: { 'Content-Type': 'application/json' }
|
|
168
|
+
}, password);
|
|
169
|
+
if (newPassword) Alpine.store('global').webuiPassword = newPassword;
|
|
170
|
+
|
|
171
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
172
|
+
Alpine.store('global').showToast(Alpine.store('global').t('claudeConfigRestored'), 'success');
|
|
173
|
+
|
|
174
|
+
// Close modal
|
|
175
|
+
document.getElementById('restore_defaults_modal').close();
|
|
176
|
+
|
|
177
|
+
// Reload the config to reflect the changes
|
|
178
|
+
await this.fetchConfig();
|
|
179
|
+
} catch (e) {
|
|
180
|
+
Alpine.store('global').showToast(Alpine.store('global').t('restoreConfigFailed') + ': ' + e.message, 'error');
|
|
181
|
+
} finally {
|
|
182
|
+
this.restoring = false;
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
|
|
186
|
+
// ==========================================
|
|
187
|
+
// Presets Management
|
|
188
|
+
// ==========================================
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Fetch all saved presets from the server
|
|
192
|
+
*/
|
|
193
|
+
async fetchPresets() {
|
|
194
|
+
const password = Alpine.store('global').webuiPassword;
|
|
195
|
+
try {
|
|
196
|
+
const { response, newPassword } = await window.utils.request('/api/claude/presets', {}, password);
|
|
197
|
+
if (newPassword) Alpine.store('global').webuiPassword = newPassword;
|
|
198
|
+
|
|
199
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
200
|
+
const data = await response.json();
|
|
201
|
+
if (data.status === 'ok') {
|
|
202
|
+
this.presets = data.presets || [];
|
|
203
|
+
// Auto-select first preset if none selected
|
|
204
|
+
if (this.presets.length > 0 && !this.selectedPresetName) {
|
|
205
|
+
this.selectedPresetName = this.presets[0].name;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
} catch (e) {
|
|
209
|
+
console.error('Failed to fetch presets:', e);
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Load the selected preset into the form (does not save to Claude CLI)
|
|
215
|
+
*/
|
|
216
|
+
loadSelectedPreset() {
|
|
217
|
+
const preset = this.presets.find(p => p.name === this.selectedPresetName);
|
|
218
|
+
if (!preset) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Merge preset config into current config.env
|
|
223
|
+
this.config.env = { ...this.config.env, ...preset.config };
|
|
224
|
+
|
|
225
|
+
// Update Gemini 1M toggle based on merged config (not just preset)
|
|
226
|
+
this.gemini1mSuffix = this.detectGemini1mSuffix();
|
|
227
|
+
|
|
228
|
+
Alpine.store('global').showToast(
|
|
229
|
+
Alpine.store('global').t('presetLoaded') || `Preset "${preset.name}" loaded. Click "Apply to Claude CLI" to save.`,
|
|
230
|
+
'success'
|
|
231
|
+
);
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Check if current config matches any saved preset
|
|
236
|
+
* @returns {boolean} True if current config matches a preset
|
|
237
|
+
*/
|
|
238
|
+
currentConfigMatchesPreset() {
|
|
239
|
+
const relevantKeys = [
|
|
240
|
+
'ANTHROPIC_BASE_URL',
|
|
241
|
+
'ANTHROPIC_AUTH_TOKEN',
|
|
242
|
+
'ANTHROPIC_MODEL',
|
|
243
|
+
'CLAUDE_CODE_SUBAGENT_MODEL',
|
|
244
|
+
'ANTHROPIC_DEFAULT_OPUS_MODEL',
|
|
245
|
+
'ANTHROPIC_DEFAULT_SONNET_MODEL',
|
|
246
|
+
'ANTHROPIC_DEFAULT_HAIKU_MODEL',
|
|
247
|
+
'ENABLE_EXPERIMENTAL_MCP_CLI'
|
|
248
|
+
];
|
|
249
|
+
|
|
250
|
+
for (const preset of this.presets) {
|
|
251
|
+
let matches = true;
|
|
252
|
+
for (const key of relevantKeys) {
|
|
253
|
+
const currentVal = this.config.env[key] || '';
|
|
254
|
+
const presetVal = preset.config[key] || '';
|
|
255
|
+
if (currentVal !== presetVal) {
|
|
256
|
+
matches = false;
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (matches) return true;
|
|
261
|
+
}
|
|
262
|
+
return false;
|
|
263
|
+
},
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Handle preset selection change - auto-load with unsaved changes warning
|
|
267
|
+
* @param {string} newPresetName - The newly selected preset name
|
|
268
|
+
*/
|
|
269
|
+
async onPresetSelect(newPresetName) {
|
|
270
|
+
if (!newPresetName || newPresetName === this.selectedPresetName) return;
|
|
271
|
+
|
|
272
|
+
// Check if current config has unsaved changes (doesn't match any preset)
|
|
273
|
+
const hasUnsavedChanges = !this.currentConfigMatchesPreset();
|
|
274
|
+
|
|
275
|
+
if (hasUnsavedChanges) {
|
|
276
|
+
// Store pending preset and show confirmation modal
|
|
277
|
+
this.pendingPresetName = newPresetName;
|
|
278
|
+
document.getElementById('unsaved_changes_modal').showModal();
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
this.selectedPresetName = newPresetName;
|
|
283
|
+
this.loadSelectedPreset();
|
|
284
|
+
},
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Confirm loading preset despite unsaved changes
|
|
288
|
+
*/
|
|
289
|
+
confirmLoadPreset() {
|
|
290
|
+
document.getElementById('unsaved_changes_modal').close();
|
|
291
|
+
this.selectedPresetName = this.pendingPresetName;
|
|
292
|
+
this.pendingPresetName = '';
|
|
293
|
+
this.loadSelectedPreset();
|
|
294
|
+
},
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Cancel loading preset - revert dropdown selection
|
|
298
|
+
*/
|
|
299
|
+
cancelLoadPreset() {
|
|
300
|
+
document.getElementById('unsaved_changes_modal').close();
|
|
301
|
+
// Revert the dropdown to current selection
|
|
302
|
+
const select = document.querySelector('[aria-label="Select preset"]');
|
|
303
|
+
if (select) select.value = this.selectedPresetName;
|
|
304
|
+
this.pendingPresetName = '';
|
|
305
|
+
},
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Save the current config as a new preset
|
|
309
|
+
*/
|
|
310
|
+
async saveCurrentAsPreset() {
|
|
311
|
+
// Clear the input and show the save preset modal
|
|
312
|
+
this.newPresetName = '';
|
|
313
|
+
document.getElementById('save_preset_modal').showModal();
|
|
314
|
+
},
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Execute preset save after user enters name
|
|
318
|
+
*/
|
|
319
|
+
async executeSavePreset(name) {
|
|
320
|
+
if (!name || !name.trim()) {
|
|
321
|
+
Alpine.store('global').showToast(Alpine.store('global').t('presetNameRequired'), 'error');
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
this.savingPreset = true;
|
|
326
|
+
const password = Alpine.store('global').webuiPassword;
|
|
327
|
+
|
|
328
|
+
try {
|
|
329
|
+
// Save only relevant env vars
|
|
330
|
+
const relevantKeys = [
|
|
331
|
+
'ANTHROPIC_BASE_URL',
|
|
332
|
+
'ANTHROPIC_AUTH_TOKEN',
|
|
333
|
+
'ANTHROPIC_MODEL',
|
|
334
|
+
'CLAUDE_CODE_SUBAGENT_MODEL',
|
|
335
|
+
'ANTHROPIC_DEFAULT_OPUS_MODEL',
|
|
336
|
+
'ANTHROPIC_DEFAULT_SONNET_MODEL',
|
|
337
|
+
'ANTHROPIC_DEFAULT_HAIKU_MODEL',
|
|
338
|
+
'ENABLE_EXPERIMENTAL_MCP_CLI'
|
|
339
|
+
];
|
|
340
|
+
const presetConfig = {};
|
|
341
|
+
relevantKeys.forEach(k => {
|
|
342
|
+
if (this.config.env[k]) {
|
|
343
|
+
presetConfig[k] = this.config.env[k];
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
const { response, newPassword } = await window.utils.request('/api/claude/presets', {
|
|
348
|
+
method: 'POST',
|
|
349
|
+
headers: { 'Content-Type': 'application/json' },
|
|
350
|
+
body: JSON.stringify({ name: name.trim(), config: presetConfig })
|
|
351
|
+
}, password);
|
|
352
|
+
if (newPassword) Alpine.store('global').webuiPassword = newPassword;
|
|
353
|
+
|
|
354
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
355
|
+
const data = await response.json();
|
|
356
|
+
if (data.status === 'ok') {
|
|
357
|
+
this.presets = data.presets || [];
|
|
358
|
+
this.selectedPresetName = name.trim();
|
|
359
|
+
this.newPresetName = ''; // Clear the input
|
|
360
|
+
Alpine.store('global').showToast(
|
|
361
|
+
Alpine.store('global').t('presetSaved') || `Preset "${name}" saved`,
|
|
362
|
+
'success'
|
|
363
|
+
);
|
|
364
|
+
document.getElementById('save_preset_modal').close();
|
|
365
|
+
} else {
|
|
366
|
+
throw new Error(data.error || Alpine.store('global').t('saveFailed'));
|
|
367
|
+
}
|
|
368
|
+
} catch (e) {
|
|
369
|
+
Alpine.store('global').showToast(Alpine.store('global').t('failedToSavePreset') + ': ' + e.message, 'error');
|
|
370
|
+
} finally {
|
|
371
|
+
this.savingPreset = false;
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Delete the selected preset
|
|
377
|
+
*/
|
|
378
|
+
async deleteSelectedPreset() {
|
|
379
|
+
if (!this.selectedPresetName) {
|
|
380
|
+
Alpine.store('global').showToast(Alpine.store('global').t('noPresetSelected'), 'warning');
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Confirm deletion
|
|
385
|
+
const confirmMsg = Alpine.store('global').t('deletePresetConfirm', { name: this.selectedPresetName });
|
|
386
|
+
if (!confirm(confirmMsg)) {
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
this.deletingPreset = true;
|
|
391
|
+
const password = Alpine.store('global').webuiPassword;
|
|
392
|
+
|
|
393
|
+
try {
|
|
394
|
+
const { response, newPassword } = await window.utils.request(
|
|
395
|
+
`/api/claude/presets/${encodeURIComponent(this.selectedPresetName)}`,
|
|
396
|
+
{ method: 'DELETE' },
|
|
397
|
+
password
|
|
398
|
+
);
|
|
399
|
+
if (newPassword) Alpine.store('global').webuiPassword = newPassword;
|
|
400
|
+
|
|
401
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
402
|
+
const data = await response.json();
|
|
403
|
+
if (data.status === 'ok') {
|
|
404
|
+
this.presets = data.presets || [];
|
|
405
|
+
// Select first available preset or clear selection
|
|
406
|
+
this.selectedPresetName = this.presets.length > 0 ? this.presets[0].name : '';
|
|
407
|
+
Alpine.store('global').showToast(
|
|
408
|
+
Alpine.store('global').t('presetDeleted') || 'Preset deleted',
|
|
409
|
+
'success'
|
|
410
|
+
);
|
|
411
|
+
} else {
|
|
412
|
+
throw new Error(data.error || Alpine.store('global').t('deleteFailed'));
|
|
413
|
+
}
|
|
414
|
+
} catch (e) {
|
|
415
|
+
Alpine.store('global').showToast(Alpine.store('global').t('failedToDeletePreset') + ': ' + e.message, 'error');
|
|
416
|
+
} finally {
|
|
417
|
+
this.deletingPreset = false;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
});
|