Skip to content

Commit feeec55

Browse files
committed
fix setting menu
1 parent dff722c commit feeec55

3 files changed

Lines changed: 46 additions & 55 deletions

File tree

src/api_key.js

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,18 @@
11
export class APIKeyManager {
22
constructor(secret) {
3-
this.secret = secret; // Secret passphrase for encryption/decryption
3+
this.secret = secret;
44
}
55

6-
// Utility to encode a string as an ArrayBuffer
76
_encode(text) {
87
const encoder = new TextEncoder();
98
return encoder.encode(text);
109
}
1110

12-
// Utility to decode an ArrayBuffer to a string
1311
_decode(buffer) {
1412
const decoder = new TextDecoder();
1513
return decoder.decode(buffer);
1614
}
1715

18-
// Utility to import a key for AES-GCM
1916
async _importKey(secret) {
2017
const keyMaterial = await crypto.subtle.importKey(
2118
"raw",
@@ -39,10 +36,9 @@ export class APIKeyManager {
3936
return key;
4037
}
4138

42-
// Encrypt a value using AES-GCM
4339
async _encrypt(value) {
4440
const key = await this._importKey(this.secret);
45-
const iv = crypto.getRandomValues(new Uint8Array(12)); // Initialization vector
41+
const iv = crypto.getRandomValues(new Uint8Array(12));
4642
const encrypted = await crypto.subtle.encrypt(
4743
{
4844
name: "AES-GCM",
@@ -54,7 +50,6 @@ export class APIKeyManager {
5450
return { iv, encrypted };
5551
}
5652

57-
// Decrypt a value using AES-GCM
5853
async _decrypt(encrypted, iv) {
5954
const key = await this._importKey(this.secret);
6055
const decrypted = await crypto.subtle.decrypt(
@@ -68,36 +63,44 @@ export class APIKeyManager {
6863
return this._decode(decrypted);
6964
}
7065

71-
// Save API key
72-
async saveAPIKey(provider, apiKey) {
73-
const { iv, encrypted } = await this._encrypt(apiKey);
66+
async saveAPIKey(provider, apiKeyOrObject) {
67+
const payload = typeof apiKeyOrObject === "string" ? { apiKey: apiKeyOrObject } : apiKeyOrObject || {};
68+
const json = JSON.stringify(payload);
69+
const { iv, encrypted } = await this._encrypt(json);
7470
const storageValue = {
7571
iv: Array.from(iv),
7672
encrypted: Array.from(new Uint8Array(encrypted))
7773
};
7874
localStorage.setItem(provider, JSON.stringify(storageValue));
7975
}
8076

81-
// Retrieve API key
8277
async getAPIKey(provider) {
78+
const creds = await this.getCredentials(provider);
79+
if (!creds) return null;
80+
return creds.apiKey || null;
81+
}
82+
83+
async getCredentials(provider) {
8384
const storageValue = localStorage.getItem(provider);
8485
if (!storageValue) {
8586
return null;
8687
}
8788
const { iv, encrypted } = JSON.parse(storageValue);
88-
const decryptedKey = await this._decrypt(
89+
const decrypted = await this._decrypt(
8990
new Uint8Array(encrypted),
9091
new Uint8Array(iv)
9192
);
92-
return decryptedKey;
93+
try {
94+
return JSON.parse(decrypted);
95+
} catch (e) {
96+
return null;
97+
}
9398
}
9499

95-
// Delete API key
96100
deleteAPIKey(provider) {
97101
localStorage.removeItem(provider);
98102
}
99103

100-
// Check if an API key exists
101104
apiKeyExists(provider) {
102105
return localStorage.getItem(provider) !== null;
103106
}

src/main.js

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import copy from "copy-to-clipboard";
99
import { v4 as uuidv4 } from "uuid";
1010
import { APIKeyManager } from "./api_key";
1111
import { copyIconSvg, sendIconSvg, stopIconSvg } from "./constants";
12+
import { getModelsFromProvider } from "./utils";
1213

1314
const fs = acode.require("fs");
1415
const select = acode.require("select");
@@ -287,7 +288,7 @@ class AIAssistant {
287288
}
288289

289290
try {
290-
const settings = this.getSettings();
291+
const settings = await this.getSettings();
291292
if (!settings.apiKey || !settings.baseUrl || !settings.model) {
292293
window.toast("Please configure API settings first", 3000);
293294
return;
@@ -376,7 +377,7 @@ class AIAssistant {
376377
if (sendBtn) sendBtn.innerHTML = stopIconSvg;
377378

378379
try {
379-
const settings = this.getSettings();
380+
const settings = await this.getSettings();
380381
if (!settings.apiKey || !settings.baseUrl || !settings.model) {
381382
window.toast("Please configure API settings first", 3000);
382383
this.switchView("settings", app);
@@ -521,11 +522,19 @@ class AIAssistant {
521522
return response;
522523
}
523524

524-
getSettings() {
525+
async getSettings() {
526+
let creds = null;
527+
if (this.apiKeyManager && typeof this.apiKeyManager.getCredentials === "function") {
528+
try {
529+
creds = await this.apiKeyManager.getCredentials("default");
530+
} catch (e) {
531+
creds = null;
532+
}
533+
}
525534
return {
526-
apiKey: localStorage.getItem("ai-api-key") || "",
527-
baseUrl: localStorage.getItem("ai-base-url") || "https://api.openai.com/v1",
528-
model: localStorage.getItem("ai-model") || "gpt-3.5-turbo",
535+
apiKey: (creds && creds.apiKey) || (localStorage.getItem("ai-api-key") || ""),
536+
baseUrl: (creds && creds.baseUrl) || (localStorage.getItem("ai-base-url") || "https://api.openai.com/v1"),
537+
model: (creds && creds.model) || (localStorage.getItem("ai-model") || "gpt-3.5-turbo"),
529538
};
530539
}
531540

@@ -542,8 +551,7 @@ class AIAssistant {
542551

543552
if (apiKey && this.apiKeyManager && typeof this.apiKeyManager.saveAPIKey === "function") {
544553
try {
545-
await this.apiKeyManager.saveAPIKey("default", apiKey);
546-
localStorage.setItem("ai-api-key", "saved");
554+
await this.apiKeyManager.saveAPIKey("default", { apiKey, baseUrl, model });
547555
} catch (e) {
548556
console.error("saveAPIKey error:", e);
549557
}
@@ -557,29 +565,16 @@ class AIAssistant {
557565

558566
async loadSettings(app) {
559567
if (!app) return;
560-
const settings = this.getSettings();
568+
const settings = await this.getSettings();
561569
const settingsForm = app.querySelector(".ai-settings-form");
562570
if (!settingsForm) return;
563571

564572
const baseUrlInput = settingsForm.querySelector("#base-url");
565573
const modelInput = settingsForm.querySelector("#model-input");
574+
const apiKeyInput = settingsForm.querySelector("#api-key");
566575
if (baseUrlInput) baseUrlInput.value = settings.baseUrl || "";
567576
if (modelInput) modelInput.value = settings.model || "";
568-
569-
if (this.apiKeyManager && typeof this.apiKeyManager.getAPIKey === "function") {
570-
try {
571-
const apiKey = await this.apiKeyManager.getAPIKey("default");
572-
if (apiKey) {
573-
const apiKeyInput = settingsForm.querySelector("#api-key");
574-
if (apiKeyInput) apiKeyInput.value = apiKey;
575-
}
576-
} catch (e) {
577-
console.error("getAPIKey error:", e);
578-
}
579-
} else {
580-
const apiKeyInput = settingsForm.querySelector("#api-key");
581-
if (apiKeyInput) apiKeyInput.value = "";
582-
}
577+
if (apiKeyInput) apiKeyInput.value = settings.apiKey || "";
583578
}
584579

585580
async loadChatHistory(app) {

src/utils.js

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { Ollama } from "ollama/browser";
2-
import { AI_PROVIDERS } from "./constants";
2+
import { AI_PROVIDERS, OPENAI_LIKE } from "./constants";
33

44
export async function getModelsFromProvider(provider, apiKey) {
55
let modelList;
66
try {
77
switch (provider) {
8-
case AI_PROVIDERS[0]: // OpenAI
8+
case AI_PROVIDERS[0]:
99
const openAIResponse = await fetch("https://api.openai.com/v1/models", {
1010
headers: {
1111
Authorization: `Bearer ${apiKey}`,
@@ -24,13 +24,12 @@ export async function getModelsFromProvider(provider, apiKey) {
2424
}
2525

2626
const openAIData = await openAIResponse.json();
27-
// filter only gpt realted models
2827
modelList = openAIData.data
2928
.filter((item) => /gpt/i.test(item.id))
3029
.map((item) => item.id);
3130
break;
3231

33-
case AI_PROVIDERS[1]: // Google AI
32+
case AI_PROVIDERS[1]:
3433
const googleAIResponse = await fetch(
3534
`https://generativelanguage.googleapis.com/v1/models?key=${apiKey}`,
3635
{
@@ -52,21 +51,18 @@ export async function getModelsFromProvider(provider, apiKey) {
5251

5352
const googleAIData = await googleAIResponse.json();
5453
modelList = googleAIData.models
55-
.filter((model) => /gemini/i.test(model.name)) // Filter models containing "gemini"
56-
.map((model) => model.name.replace(/^models\//, "")); // Remove "models/" prefix
57-
54+
.filter((model) => /gemini/i.test(model.name))
55+
.map((model) => model.name.replace(/^models\//, ""));
5856
break;
59-
case AI_PROVIDERS[2]: // ollama
60-
// check local storage, if user want to provide custom host for ollama
57+
case AI_PROVIDERS[2]:
6158
let host = window.localStorage.getItem("Ollama-Host")
6259
? window.localStorage.getItem("Ollama-Host")
6360
: "http://localhost:11434";
6461
const ollama = new Ollama({ host });
6562
const list = await ollama.list();
6663
modelList = list.models.map((item) => item.model);
6764
break;
68-
69-
case AI_PROVIDERS[3]: // Groq
65+
case AI_PROVIDERS[3]:
7066
const groqAIResponse = await fetch(
7167
`https://api.groq.com/openai/v1/models`,
7268
{
@@ -90,11 +86,8 @@ export async function getModelsFromProvider(provider, apiKey) {
9086
const groqAIData = await groqAIResponse.json();
9187
modelList = groqAIData.data.map((item) => item.id);
9288
break;
93-
94-
case OPENAI_LIKE: // OpenAI-Like
95-
// Return empty array because we do not fetch models for this
89+
case OPENAI_LIKE:
9690
return [];
97-
9891
default:
9992
throw new Error(`Unsupported provider: ${provider}`);
10093
}
@@ -104,4 +97,4 @@ export async function getModelsFromProvider(provider, apiKey) {
10497
}
10598

10699
return modelList;
107-
}
100+
}

0 commit comments

Comments
 (0)