From 3bb350f3cfdd84a5fe2a1ccd504e79be6d421b5d Mon Sep 17 00:00:00 2001 From: Peisong Xiao Date: Sat, 17 Jan 2026 16:12:41 -0500 Subject: [PATCH] Added multi-key support --- wwcompanion-extension/background.js | 13 +++ wwcompanion-extension/manifest.json | 2 +- wwcompanion-extension/popup.js | 36 ++++-- wwcompanion-extension/settings.css | 24 ++++ wwcompanion-extension/settings.html | 24 +++- wwcompanion-extension/settings.js | 170 ++++++++++++++++++++++++++-- 6 files changed, 244 insertions(+), 25 deletions(-) diff --git a/wwcompanion-extension/background.js b/wwcompanion-extension/background.js index fb8b610..a76ab6e 100644 --- a/wwcompanion-extension/background.js +++ b/wwcompanion-extension/background.js @@ -15,6 +15,8 @@ const DEFAULT_TASKS = [ const DEFAULT_SETTINGS = { apiKey: "", + apiKeys: [], + activeApiKeyId: "", apiBaseUrl: "https://api.openai.com/v1", apiKeyHeader: "Authorization", apiKeyPrefix: "Bearer ", @@ -80,6 +82,17 @@ chrome.runtime.onInstalled.addListener(async () => { if (missing) updates[key] = value; } + const hasApiKeys = + Array.isArray(stored.apiKeys) && stored.apiKeys.length > 0; + + if (!hasApiKeys && stored.apiKey) { + const id = crypto?.randomUUID + ? crypto.randomUUID() + : `key-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`; + updates.apiKeys = [{ id, name: "Default", key: stored.apiKey }]; + updates.activeApiKeyId = id; + } + if (Object.keys(updates).length) { await chrome.storage.local.set(updates); } diff --git a/wwcompanion-extension/manifest.json b/wwcompanion-extension/manifest.json index c0fd612..ad3f641 100644 --- a/wwcompanion-extension/manifest.json +++ b/wwcompanion-extension/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "WWCompanion", - "version": "0.2.3", + "version": "0.2.4", "description": "AI companion for WaterlooWorks job postings.", "permissions": ["storage", "activeTab"], "host_permissions": ["https://waterlooworks.uwaterloo.ca/*"], diff --git a/wwcompanion-extension/popup.js b/wwcompanion-extension/popup.js index 293a044..e251a6b 100644 --- a/wwcompanion-extension/popup.js +++ b/wwcompanion-extension/popup.js @@ -428,24 +428,38 @@ async function handleAnalyze() { return; } - const { apiKey, apiBaseUrl, apiKeyHeader, apiKeyPrefix, model, systemPrompt, resume } = - await getStorage([ - "apiKey", - "apiBaseUrl", - "apiKeyHeader", - "apiKeyPrefix", - "model", - "systemPrompt", - "resume" - ]); + const { + apiKeys = [], + activeApiKeyId = "", + apiBaseUrl, + apiKeyHeader, + apiKeyPrefix, + model, + systemPrompt, + resume + } = await getStorage([ + "apiKeys", + "activeApiKeyId", + "apiBaseUrl", + "apiKeyHeader", + "apiKeyPrefix", + "model", + "systemPrompt", + "resume" + ]); if (!apiBaseUrl) { setStatus("Set an API base URL in Settings."); return; } + const resolvedKeys = Array.isArray(apiKeys) ? apiKeys : []; + const activeKey = + resolvedKeys.find((entry) => entry.id === activeApiKeyId) || resolvedKeys[0]; + const apiKey = activeKey?.key || ""; + if (apiKeyHeader && !apiKey) { - setStatus("Add your API key in Settings."); + setStatus("Add an API key in Settings."); return; } diff --git a/wwcompanion-extension/settings.css b/wwcompanion-extension/settings.css index 3d0c33d..27bcc07 100644 --- a/wwcompanion-extension/settings.css +++ b/wwcompanion-extension/settings.css @@ -229,6 +229,30 @@ button:active { gap: 8px; } +.api-keys { + display: grid; + gap: 12px; +} + +.api-key-card { + padding: 12px; + border-radius: 12px; + border: 1px solid var(--border); + background: var(--card-bg); + display: grid; + gap: 8px; +} + +.api-key-actions { + display: flex; + gap: 8px; + justify-content: flex-end; +} + +.api-key-actions .delete { + color: #c0392b; +} + @media (prefers-color-scheme: dark) { :root:not([data-theme]), :root[data-theme="system"] { diff --git a/wwcompanion-extension/settings.html b/wwcompanion-extension/settings.html index 4a144af..86babd3 100644 --- a/wwcompanion-extension/settings.html +++ b/wwcompanion-extension/settings.html @@ -30,11 +30,8 @@
- -
- - -
+ +
@@ -59,6 +56,23 @@
+
+ + +

API Keys

+
+
+
+
+ +
+
+
+
+