|
54 | 54 | <div class="settings-section"> |
55 | 55 | <div class="settings-section-header"> AI Intelligence</div> |
56 | 56 | <div class="settings-section-body"> |
| 57 | + <div class="settings-item"> |
| 58 | + <div class="settings-label"> |
| 59 | + <div class="settings-title">OpenCode API Key</div> |
| 60 | + <div class="settings-hint">Default free provider. Get a key at <a href="https://opencode.ai/zen" target="_blank" rel="noopener">opencode.ai/zen</a>. ${cfg.opencode_key_set ? '<span class="badge badge-done">configured</span>' : '<span class="badge badge-failed">not set</span>'}</div> |
| 61 | + </div> |
| 62 | + <div class="settings-control"> |
| 63 | + <input type="password" id="opencode-key-input" |
| 64 | + value="" |
| 65 | + placeholder="${cfg.opencode_key_set ? '••••••• (saved)' : 'oc-...'}" |
| 66 | + class="form-control premium-input"> |
| 67 | + <button class="btn btn-primary" onclick="window.SettingsPage.saveOpenCodeKey()">Save</button> |
| 68 | + </div> |
| 69 | + </div> |
| 70 | + <div class="settings-item"> |
| 71 | + <div class="settings-label"> |
| 72 | + <div class="settings-title">OpenCode Model</div> |
| 73 | + <div class="settings-hint">Override the default model. Leave blank or type <code>default</code> to use <code>deepseek-v4-flash-free</code>. See <a href="https://opencode.ai/zen/v1/models" target="_blank" rel="noopener">available models</a>.</div> |
| 74 | + </div> |
| 75 | + <div class="settings-control"> |
| 76 | + <input type="text" id="opencode-model-input" |
| 77 | + value="${escValue(cfg.opencode_model || '')}" |
| 78 | + placeholder="deepseek-v4-flash-free" |
| 79 | + class="form-control premium-input"> |
| 80 | + <button class="btn btn-primary" onclick="window.SettingsPage.saveOpenCodeModel()">Save</button> |
| 81 | + </div> |
| 82 | + </div> |
57 | 83 | <div class="settings-item"> |
58 | 84 | <div class="settings-label"> |
59 | 85 | <div class="settings-title">OpenRouter API Key</div> |
60 | | - <div class="settings-hint">Used for vulnerability validation and reporting.</div> |
| 86 | + <div class="settings-hint">Optional — used when set, for premium or alternative models. ${cfg.openrouter_key_set ? '<span class="badge badge-done">configured</span>' : '<span class="badge badge-failed">not set</span>'}</div> |
61 | 87 | </div> |
62 | 88 | <div class="settings-control"> |
63 | 89 | <input type="password" id="or-key-input" |
64 | | - value="${escValue(localStorage.getItem('autoar_or_key') || '')}" |
65 | | - placeholder="sk-or-v1-…" |
| 90 | + value="" |
| 91 | + placeholder="${cfg.openrouter_key_set ? '••••••• (saved)' : 'sk-or-v1-…'}" |
66 | 92 | class="form-control premium-input"> |
67 | 93 | <button class="btn btn-primary" onclick="window.SettingsPage.saveOpenRouterKey()">Save</button> |
68 | 94 | </div> |
69 | 95 | </div> |
| 96 | + <div class="settings-item"> |
| 97 | + <div class="settings-label"> |
| 98 | + <div class="settings-title">OpenRouter Model</div> |
| 99 | + <div class="settings-hint">Override the default model. Leave blank or type <code>default</code> to use <code>z-ai/glm-4.5-air:free</code>.</div> |
| 100 | + </div> |
| 101 | + <div class="settings-control"> |
| 102 | + <input type="text" id="openrouter-model-input" |
| 103 | + value="${escValue(cfg.openrouter_model || '')}" |
| 104 | + placeholder="z-ai/glm-4.5-air:free" |
| 105 | + class="form-control premium-input"> |
| 106 | + <button class="btn btn-primary" onclick="window.SettingsPage.saveOpenRouterModel()">Save</button> |
| 107 | + </div> |
| 108 | + </div> |
70 | 109 | <div class="settings-item"> |
71 | 110 | <div class="settings-label"> |
72 | 111 | <div class="settings-title">Gemini API Key</div> |
73 | | - <div class="settings-hint">Secondary fallback for AI analysis.</div> |
| 112 | + <div class="settings-hint">Final fallback for AI analysis. ${cfg.gemini_key_set ? '<span class="badge badge-done">configured</span>' : '<span class="badge badge-failed">not set</span>'}</div> |
74 | 113 | </div> |
75 | 114 | <div class="settings-control"> |
76 | 115 | <input type="password" id="gemini-key-input" |
77 | | - value="${escValue(localStorage.getItem('autoar_gemini_key') || '')}" |
78 | | - placeholder="AIza…" |
| 116 | + value="" |
| 117 | + placeholder="${cfg.gemini_key_set ? '••••••• (saved)' : 'AIza…'}" |
79 | 118 | class="form-control premium-input"> |
80 | 119 | <button class="btn btn-primary" onclick="window.SettingsPage.saveGeminiKey()">Save</button> |
81 | 120 | </div> |
|
179 | 218 |
|
180 | 219 | if (key) localStorage.setItem('autoar_or_key', key); |
181 | 220 | else localStorage.removeItem('autoar_or_key'); |
182 | | - |
| 221 | + |
183 | 222 | window.showToast('success', 'Saved!', 'OpenRouter key updated on server.'); |
| 223 | + input.value = ''; |
| 224 | + try { window.state.config = await window.apiFetch('/api/config'); renderSettings(); } catch(_) {} |
184 | 225 | } catch (e) { |
185 | 226 | window.showToast('error', 'Error', e.message); |
186 | 227 | } |
|
198 | 239 | body: JSON.stringify({ gemini_key: key }) |
199 | 240 | }); |
200 | 241 | if (!res.ok) throw new Error('Failed to update server config'); |
201 | | - |
| 242 | + |
202 | 243 | if (key) localStorage.setItem('autoar_gemini_key', key); |
203 | 244 | else localStorage.removeItem('autoar_gemini_key'); |
204 | | - |
| 245 | + |
205 | 246 | window.showToast('success', 'Saved!', 'Gemini key updated on server.'); |
| 247 | + input.value = ''; |
| 248 | + try { window.state.config = await window.apiFetch('/api/config'); renderSettings(); } catch(_) {} |
| 249 | + } catch (e) { |
| 250 | + window.showToast('error', 'Error', e.message); |
| 251 | + } |
| 252 | + } |
| 253 | + |
| 254 | + async function saveOpenCodeKey() { |
| 255 | + const input = document.getElementById('opencode-key-input'); |
| 256 | + if (!input) return; |
| 257 | + const key = input.value.trim(); |
| 258 | + if (!key) { |
| 259 | + window.showToast('error', 'Empty key', 'Enter an OpenCode API key before saving.'); |
| 260 | + return; |
| 261 | + } |
| 262 | + try { |
| 263 | + const headers = await window.buildAuthHeaders({ 'Content-Type': 'application/json' }); |
| 264 | + const res = await fetch('/api/settings', { |
| 265 | + method: 'POST', |
| 266 | + headers, |
| 267 | + body: JSON.stringify({ opencode_key: key }) |
| 268 | + }); |
| 269 | + if (!res.ok) throw new Error('Failed to update server config'); |
| 270 | + window.showToast('success', 'Saved!', 'OpenCode key updated on server.'); |
| 271 | + input.value = ''; |
| 272 | + try { window.state.config = await window.apiFetch('/api/config'); renderSettings(); } catch(_) {} |
| 273 | + } catch (e) { |
| 274 | + window.showToast('error', 'Error', e.message); |
| 275 | + } |
| 276 | + } |
| 277 | + |
| 278 | + async function saveOpenCodeModel() { |
| 279 | + const input = document.getElementById('opencode-model-input'); |
| 280 | + if (!input) return; |
| 281 | + const model = input.value.trim(); |
| 282 | + try { |
| 283 | + const headers = await window.buildAuthHeaders({ 'Content-Type': 'application/json' }); |
| 284 | + const res = await fetch('/api/settings', { |
| 285 | + method: 'POST', |
| 286 | + headers, |
| 287 | + body: JSON.stringify({ opencode_model: model }) |
| 288 | + }); |
| 289 | + if (!res.ok) throw new Error('Failed to update OpenCode model'); |
| 290 | + window.showToast('success', 'Saved!', model ? `OpenCode model set to "${model}".` : 'OpenCode model reset to default.'); |
| 291 | + try { window.state.config = await window.apiFetch('/api/config'); renderSettings(); } catch(_) {} |
| 292 | + } catch (e) { |
| 293 | + window.showToast('error', 'Error', e.message); |
| 294 | + } |
| 295 | + } |
| 296 | + |
| 297 | + async function saveOpenRouterModel() { |
| 298 | + const input = document.getElementById('openrouter-model-input'); |
| 299 | + if (!input) return; |
| 300 | + const model = input.value.trim(); |
| 301 | + try { |
| 302 | + const headers = await window.buildAuthHeaders({ 'Content-Type': 'application/json' }); |
| 303 | + const res = await fetch('/api/settings', { |
| 304 | + method: 'POST', |
| 305 | + headers, |
| 306 | + body: JSON.stringify({ openrouter_model: model }) |
| 307 | + }); |
| 308 | + if (!res.ok) throw new Error('Failed to update OpenRouter model'); |
| 309 | + window.showToast('success', 'Saved!', model ? `OpenRouter model set to "${model}".` : 'OpenRouter model reset to default.'); |
| 310 | + try { window.state.config = await window.apiFetch('/api/config'); renderSettings(); } catch(_) {} |
206 | 311 | } catch (e) { |
207 | 312 | window.showToast('error', 'Error', e.message); |
208 | 313 | } |
|
276 | 381 | loadConfig, |
277 | 382 | renderSettings, |
278 | 383 | saveOpenRouterKey, |
| 384 | + saveOpenCodeKey, |
| 385 | + saveOpenCodeModel, |
| 386 | + saveOpenRouterModel, |
279 | 387 | saveGeminiKey, |
280 | 388 | saveTimeoutSettings, |
281 | 389 | saveWebhookSettings, |
|
0 commit comments