diff --git a/app/L0/_all/mod/_core/admin/views/agent/api.js b/app/L0/_all/mod/_core/admin/views/agent/api.js index e36a2f31..1bec08ab 100644 --- a/app/L0/_all/mod/_core/admin/views/agent/api.js +++ b/app/L0/_all/mod/_core/admin/views/agent/api.js @@ -6,12 +6,14 @@ import { mergeConsecutiveChatMessages } from "/mod/_core/framework/js/chat-messa import * as proxyUrl from "/mod/_core/framework/js/proxy-url.js"; import { getHuggingFaceManager } from "/mod/_core/huggingface/manager.js"; -function createHeaders(apiKey) { +function createHeaders(apiKey, bearerToken) { const headers = { "Content-Type": "application/json" }; - if (apiKey) { + if (bearerToken) { + headers.Authorization = `Bearer ${bearerToken}`; + } else if (apiKey) { headers.Authorization = `Bearer ${apiKey}`; } @@ -442,7 +444,10 @@ export const prepareAdminAgentApiRequest = globalThis.space.extend( return { apiEndpoint, - headers: createHeaders(String(effectiveSettings?.apiKey || "").trim()), + headers: createHeaders( + String(effectiveSettings?.apiKey || "").trim(), + String(effectiveSettings?.bearerToken || "").trim() + ), messages: Array.isArray(messages) ? messages : [], method: "POST", promptContext: normalizedPromptContext, @@ -461,8 +466,8 @@ async function streamAdminAgentApiCompletion({ promptContext, settings, systemPr throw new Error("Set an API endpoint before sending a message."); } - if (!settings.apiKey.trim()) { - throw new Error("Set an API key before sending a message."); + if (!settings.apiKey.trim() && !(settings.bearerToken || "").trim()) { + throw new Error("Set an API key or authorization bearer token before sending a message."); } if (!settings.model.trim()) { diff --git a/app/L0/_all/mod/_core/admin/views/agent/config.js b/app/L0/_all/mod/_core/admin/views/agent/config.js index 2a00ef4a..35d4c655 100644 --- a/app/L0/_all/mod/_core/admin/views/agent/config.js +++ b/app/L0/_all/mod/_core/admin/views/agent/config.js @@ -15,6 +15,7 @@ export const ADMIN_CHAT_LOCAL_PROVIDER = { export const DEFAULT_ADMIN_CHAT_SETTINGS = { apiEndpoint: "https://openrouter.ai/api/v1/chat/completions", apiKey: "", + bearerToken: "", huggingfaceDtype: "q4", huggingfaceModel: "", localProvider: ADMIN_CHAT_LOCAL_PROVIDER.HUGGINGFACE, diff --git a/app/L0/_all/mod/_core/admin/views/agent/panel.html b/app/L0/_all/mod/_core/admin/views/agent/panel.html index bf67faaa..e5cb464f 100644 --- a/app/L0/_all/mod/_core/admin/views/agent/panel.html +++ b/app/L0/_all/mod/_core/admin/views/agent/panel.html @@ -210,6 +210,13 @@

Provider and model configuration

API Key +
diff --git a/app/L0/_all/mod/_core/admin/views/agent/storage.js b/app/L0/_all/mod/_core/admin/views/agent/storage.js index 2dbeb99d..ebab0493 100644 --- a/app/L0/_all/mod/_core/admin/views/agent/storage.js +++ b/app/L0/_all/mod/_core/admin/views/agent/storage.js @@ -138,11 +138,16 @@ async function normalizeStoredConfig(runtime, parsedConfig) { runtime, storedConfig.api_key || storedConfig.apiKey || config.DEFAULT_ADMIN_CHAT_SETTINGS.apiKey || "" ); + const storedBearerToken = await decodeStoredApiKey( + runtime, + storedConfig.bearer_token || storedConfig.bearerToken || config.DEFAULT_ADMIN_CHAT_SETTINGS.bearerToken || "" + ); return { settings: { apiEndpoint: String(storedConfig.api_endpoint || storedConfig.apiEndpoint || config.DEFAULT_ADMIN_CHAT_SETTINGS.apiEndpoint || "").trim(), apiKey: storedApiKey.value, + bearerToken: storedBearerToken.value, huggingfaceDtype: String( storedConfig.huggingface_dtype || storedConfig.huggingfaceDtype || config.DEFAULT_ADMIN_CHAT_SETTINGS.huggingfaceDtype || "" ).trim(), @@ -156,7 +161,9 @@ async function normalizeStoredConfig(runtime, parsedConfig) { promptBudgetRatios: normalizeStoredPromptBudgetRatios(storedConfig), provider, storedApiKeyLocked: storedApiKey.locked, - storedApiKeyValue: storedApiKey.storedValue + storedApiKeyValue: storedApiKey.storedValue, + storedBearerTokenLocked: storedBearerToken.locked, + storedBearerTokenValue: storedBearerToken.storedValue }, systemPrompt: String( storedConfig.custom_system_prompt || @@ -173,6 +180,11 @@ async function buildStoredConfigPayload(runtime, { settings, systemPrompt }) { const payload = { api_endpoint: String(settings?.apiEndpoint || config.DEFAULT_ADMIN_CHAT_SETTINGS.apiEndpoint || "").trim(), api_key: await encodeStoredApiKey(runtime, settings), + bearer_token: await encodeStoredApiKey(runtime, { + apiKey: settings?.bearerToken, + storedApiKeyValue: settings?.storedBearerTokenValue, + storedApiKeyLocked: settings?.storedBearerTokenLocked + }), huggingface_dtype: String(settings?.huggingfaceDtype || config.DEFAULT_ADMIN_CHAT_SETTINGS.huggingfaceDtype || "").trim(), huggingface_model: String(settings?.huggingfaceModel || config.DEFAULT_ADMIN_CHAT_SETTINGS.huggingfaceModel || "").trim(), local_provider: config.normalizeAdminChatLocalProvider(settings?.localProvider), @@ -220,6 +232,8 @@ export async function saveAdminChatConfig(nextConfig) { if (nextConfig?.settings && typeof nextConfig.settings === "object") { nextConfig.settings.storedApiKeyLocked = false; nextConfig.settings.storedApiKeyValue = String(payload.api_key || "").trim(); + nextConfig.settings.storedBearerTokenLocked = false; + nextConfig.settings.storedBearerTokenValue = String(payload.bearer_token || "").trim(); } } catch (error) { throw new Error(`Unable to save admin chat config: ${error.message}`); diff --git a/app/L0/_all/mod/_core/admin/views/agent/store.js b/app/L0/_all/mod/_core/admin/views/agent/store.js index 4aacf7b4..1cefa25e 100644 --- a/app/L0/_all/mod/_core/admin/views/agent/store.js +++ b/app/L0/_all/mod/_core/admin/views/agent/store.js @@ -1877,6 +1877,7 @@ const model = { this.settings = { apiEndpoint: (this.settingsDraft.apiEndpoint || "").trim(), apiKey: (this.settingsDraft.apiKey || "").trim(), + bearerToken: (this.settingsDraft.bearerToken || "").trim(), huggingfaceDtype: (this.settingsDraft.huggingfaceDtype || "").trim(), huggingfaceModel: normalizeHuggingFaceModelInput(this.settingsDraft.huggingfaceModel || ""), localProvider, @@ -1886,7 +1887,9 @@ const model = { promptBudgetRatios: clonePromptBudgetRatios(this.settingsDraft.promptBudgetRatios), provider, storedApiKeyLocked: this.settings.storedApiKeyLocked === true, - storedApiKeyValue: String(this.settings.storedApiKeyValue || "") + storedApiKeyValue: String(this.settings.storedApiKeyValue || ""), + storedBearerTokenLocked: this.settings.storedBearerTokenLocked === true, + storedBearerTokenValue: String(this.settings.storedBearerTokenValue || "") }; try { diff --git a/app/L0/_all/mod/_core/onscreen_agent/api.js b/app/L0/_all/mod/_core/onscreen_agent/api.js index 510d23a6..e9367cf0 100644 --- a/app/L0/_all/mod/_core/onscreen_agent/api.js +++ b/app/L0/_all/mod/_core/onscreen_agent/api.js @@ -211,12 +211,14 @@ function normalizeCompletionMessagesForLocal(messages) { .filter(Boolean); } -function createApiRequestHeaders(apiKey) { +function createApiRequestHeaders(apiKey, bearerToken) { const headers = { "Content-Type": "application/json" }; - if (apiKey) { + if (bearerToken) { + headers.Authorization = `Bearer ${bearerToken}`; + } else if (apiKey) { headers.Authorization = `Bearer ${apiKey}`; } @@ -303,8 +305,8 @@ export class OnscreenAgentApiLlmClient extends OnscreenAgentLlmClient { throw new Error("Set an API endpoint before sending a message."); } - if (!settings.apiKey.trim()) { - throw new Error("Set an API key before sending a message."); + if (!settings.apiKey.trim() && !(settings.bearerToken || "").trim()) { + throw new Error("Set an API key or authorization bearer token before sending a message."); } if (!settings.model.trim()) { @@ -421,7 +423,10 @@ export const prepareOnscreenAgentApiRequest = globalThis.space.extend( return { apiEndpoint, - headers: createApiRequestHeaders(String(effectiveSettings?.apiKey || "").trim()), + headers: createApiRequestHeaders( + String(effectiveSettings?.apiKey || "").trim(), + String(effectiveSettings?.bearerToken || "").trim() + ), messages: Array.isArray(effectivePreparedRequest?.messages) ? effectivePreparedRequest.messages : [], method: "POST", preparedRequest: effectivePreparedRequest, diff --git a/app/L0/_all/mod/_core/onscreen_agent/config.js b/app/L0/_all/mod/_core/onscreen_agent/config.js index bcca393a..9d5e92e8 100644 --- a/app/L0/_all/mod/_core/onscreen_agent/config.js +++ b/app/L0/_all/mod/_core/onscreen_agent/config.js @@ -21,6 +21,7 @@ export const ONSCREEN_AGENT_HIDDEN_EDGE = Object.freeze({ export const DEFAULT_ONSCREEN_AGENT_SETTINGS = { apiEndpoint: "https://openrouter.ai/api/v1/chat/completions", apiKey: "", + bearerToken: "", huggingfaceDtype: "q4", huggingfaceModel: "", localProvider: ONSCREEN_AGENT_LOCAL_PROVIDER.HUGGINGFACE, diff --git a/app/L0/_all/mod/_core/onscreen_agent/panel.html b/app/L0/_all/mod/_core/onscreen_agent/panel.html index a4510bdc..81f7cbe5 100644 --- a/app/L0/_all/mod/_core/onscreen_agent/panel.html +++ b/app/L0/_all/mod/_core/onscreen_agent/panel.html @@ -360,6 +360,13 @@

Model, credentials, params, and instructions

API Key +
diff --git a/app/L0/_all/mod/_core/onscreen_agent/storage.js b/app/L0/_all/mod/_core/onscreen_agent/storage.js index 6a9b22f2..185431b5 100644 --- a/app/L0/_all/mod/_core/onscreen_agent/storage.js +++ b/app/L0/_all/mod/_core/onscreen_agent/storage.js @@ -202,6 +202,10 @@ async function normalizeStoredConfig(runtime, parsedConfig) { runtime, storedConfig.api_key || storedConfig.apiKey || config.DEFAULT_ONSCREEN_AGENT_SETTINGS.apiKey || "" ); + const storedBearerToken = await decodeStoredApiKey( + runtime, + storedConfig.bearer_token || storedConfig.bearerToken || config.DEFAULT_ONSCREEN_AGENT_SETTINGS.bearerToken || "" + ); const legacyDisplayMode = storedConfig.collapsed === true ? DISPLAY_MODE_COMPACT @@ -213,6 +217,7 @@ async function normalizeStoredConfig(runtime, parsedConfig) { settings: { apiEndpoint: String(storedConfig.api_endpoint || storedConfig.apiEndpoint || config.DEFAULT_ONSCREEN_AGENT_SETTINGS.apiEndpoint || "").trim(), apiKey: storedApiKey.value, + bearerToken: storedBearerToken.value, huggingfaceDtype: String( storedConfig.huggingface_dtype || storedConfig.huggingfaceDtype || @@ -232,7 +237,9 @@ async function normalizeStoredConfig(runtime, parsedConfig) { promptBudgetRatios: normalizeStoredPromptBudgetRatios(storedConfig), provider, storedApiKeyLocked: storedApiKey.locked, - storedApiKeyValue: storedApiKey.storedValue + storedApiKeyValue: storedApiKey.storedValue, + storedBearerTokenLocked: storedBearerToken.locked, + storedBearerTokenValue: storedBearerToken.storedValue }, systemPrompt: String( storedConfig.custom_system_prompt || @@ -278,6 +285,11 @@ async function buildStoredConfigPayload(runtime, { settings, systemPrompt }) { const payload = { api_endpoint: String(settings?.apiEndpoint || config.DEFAULT_ONSCREEN_AGENT_SETTINGS.apiEndpoint || "").trim(), api_key: await encodeStoredApiKey(runtime, settings), + bearer_token: await encodeStoredApiKey(runtime, { + apiKey: settings?.bearerToken, + storedApiKeyValue: settings?.storedBearerTokenValue, + storedApiKeyLocked: settings?.storedBearerTokenLocked + }), huggingface_dtype: String(settings?.huggingfaceDtype || config.DEFAULT_ONSCREEN_AGENT_SETTINGS.huggingfaceDtype || "").trim(), huggingface_model: String(settings?.huggingfaceModel || config.DEFAULT_ONSCREEN_AGENT_SETTINGS.huggingfaceModel || "").trim(), local_provider: config.normalizeOnscreenAgentLocalProvider(settings?.localProvider), @@ -446,6 +458,8 @@ export async function saveOnscreenAgentConfig(nextConfig) { if (nextConfig?.settings && typeof nextConfig.settings === "object") { nextConfig.settings.storedApiKeyLocked = false; nextConfig.settings.storedApiKeyValue = String(payload.api_key || "").trim(); + nextConfig.settings.storedBearerTokenLocked = false; + nextConfig.settings.storedBearerTokenValue = String(payload.bearer_token || "").trim(); } } catch (error) { throw new Error(`Unable to save onscreen agent config: ${error.message}`); diff --git a/app/L0/_all/mod/_core/onscreen_agent/store.js b/app/L0/_all/mod/_core/onscreen_agent/store.js index ae4fba33..ac4bf281 100644 --- a/app/L0/_all/mod/_core/onscreen_agent/store.js +++ b/app/L0/_all/mod/_core/onscreen_agent/store.js @@ -4604,6 +4604,7 @@ const model = { this.settings = { apiEndpoint: (this.settingsDraft.apiEndpoint || "").trim(), apiKey: (this.settingsDraft.apiKey || "").trim(), + bearerToken: (this.settingsDraft.bearerToken || "").trim(), huggingfaceDtype: (this.settingsDraft.huggingfaceDtype || "").trim(), huggingfaceModel: normalizeHuggingFaceModelInput(this.settingsDraft.huggingfaceModel || ""), localProvider, @@ -4613,7 +4614,9 @@ const model = { promptBudgetRatios: clonePromptBudgetRatios(this.settingsDraft.promptBudgetRatios), provider, storedApiKeyLocked: this.settings.storedApiKeyLocked === true, - storedApiKeyValue: String(this.settings.storedApiKeyValue || "") + storedApiKeyValue: String(this.settings.storedApiKeyValue || ""), + storedBearerTokenLocked: this.settings.storedBearerTokenLocked === true, + storedBearerTokenValue: String(this.settings.storedBearerTokenValue || "") }; this.systemPrompt = draftPrompt; this.systemPromptDraft = draftPrompt;