diff --git a/index.js b/index.js index d981220..ce7fcf4 100644 --- a/index.js +++ b/index.js @@ -633,13 +633,17 @@ async function initUI() { }); $('#rpg-external-api-key').on('change', function() { + // Securely store API key in localStorage instead of shared extension settings + const apiKey = String($(this).val()).trim(); + localStorage.setItem('rpg_companion_external_api_key', apiKey); + + // Ensure the externalApiSettings object exists, but don't store the key in it if (!extensionSettings.externalApiSettings) { extensionSettings.externalApiSettings = { - baseUrl: '', apiKey: '', model: '', maxTokens: 8192, temperature: 0.7 + baseUrl: '', model: '', maxTokens: 8192, temperature: 0.7 }; + saveSettings(); } - extensionSettings.externalApiSettings.apiKey = String($(this).val()).trim(); - saveSettings(); }); $('#rpg-external-model').on('change', function() { @@ -771,7 +775,11 @@ async function initUI() { // Initialize External API settings values if (extensionSettings.externalApiSettings) { $('#rpg-external-base-url').val(extensionSettings.externalApiSettings.baseUrl || ''); - $('#rpg-external-api-key').val(extensionSettings.externalApiSettings.apiKey || ''); + + // Load API Key from secure localStorage + const storedApiKey = localStorage.getItem('rpg_companion_external_api_key') || ''; + $('#rpg-external-api-key').val(storedApiKey); + $('#rpg-external-model').val(extensionSettings.externalApiSettings.model || ''); $('#rpg-external-max-tokens').val(extensionSettings.externalApiSettings.maxTokens || 8192); $('#rpg-external-temperature').val(extensionSettings.externalApiSettings.temperature ?? 0.7); diff --git a/src/core/state.js b/src/core/state.js index b7a6377..0941ea7 100644 --- a/src/core/state.js +++ b/src/core/state.js @@ -175,7 +175,7 @@ export let extensionSettings = { // External API settings for 'external' generation mode externalApiSettings: { baseUrl: '', // OpenAI-compatible API base URL (e.g., "https://api.openai.com/v1") - apiKey: '', // API key for the external service + // apiKey is NOT stored here for security. It is stored in localStorage('rpg_companion_api_key') model: '', // Model identifier (e.g., "gpt-4o-mini") maxTokens: 8192, // Maximum tokens for generation temperature: 0.7 // Temperature setting for generation diff --git a/src/systems/generation/apiClient.js b/src/systems/generation/apiClient.js index 894997b..34254ac 100644 --- a/src/systems/generation/apiClient.js +++ b/src/systems/generation/apiClient.js @@ -39,14 +39,16 @@ let originalPresetName = null; * @throws {Error} If the API call fails or configuration is invalid */ export async function generateWithExternalAPI(messages) { - const { baseUrl, apiKey, model, maxTokens, temperature } = extensionSettings.externalApiSettings || {}; + const { baseUrl, model, maxTokens, temperature } = extensionSettings.externalApiSettings || {}; + // Retrieve API key from secure storage (not shared extension settings) + const apiKey = localStorage.getItem('rpg_companion_external_api_key'); // Validate required settings if (!baseUrl || !baseUrl.trim()) { throw new Error('External API base URL is not configured'); } if (!apiKey || !apiKey.trim()) { - throw new Error('External API key is not configured'); + throw new Error('External API key is not found. If you switched browsers or cleared your cache, please re-enter your API key in the extension settings.'); } if (!model || !model.trim()) { throw new Error('External API model is not configured'); @@ -113,12 +115,15 @@ export async function generateWithExternalAPI(messages) { * @returns {Promise<{success: boolean, message: string, model?: string}>} */ export async function testExternalAPIConnection() { - const { baseUrl, apiKey, model } = extensionSettings.externalApiSettings || {}; + const { baseUrl, model } = extensionSettings.externalApiSettings || {}; + const apiKey = localStorage.getItem('rpg_companion_external_api_key'); if (!baseUrl || !apiKey || !model) { return { success: false, - message: 'Please fill in all required fields (Base URL, API Key, and Model)' + message: !apiKey + ? 'API Key not found. Please re-enter it in settings (keys are stored locally per-browser).' + : 'Please fill in all required fields (Base URL, API Key, and Model)' }; }