feat: add custom/other OpenAI-compatible LLM providers

Allow Settings → Providers to define custom providers (id, name, base URL,
API key, models, headers) without code changes. Persist config via OpenCode
layers, store keys through auth.set, and keep web/VS Code parity.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
This commit is contained in:
Cursor Agent
2026-08-02 09:12:28 +00:00
co-authored by Serhii Dziupin
parent 5d24d6cb2a
commit be87e25c7d
25 changed files with 1980 additions and 33 deletions
@@ -1377,6 +1377,46 @@ export const settingsDict = {
'settings.providers.page.auth.title': 'Uwierzytelnianie',
'settings.providers.page.auth.useReconnectHint': '· Użyj Połącz ponownie, aby zaktualizować dane logowania',
'settings.providers.page.connect.allProvidersConnected': 'Wszyscy dostawcy są połączeni.',
'settings.providers.page.custom.optionLabel': 'Inny / Niestandardowy',
'settings.providers.page.custom.title': 'Niestandardowy dostawca',
'settings.providers.page.custom.description': 'Dodaj dostawcę zgodnego z OpenAI, podając adres bazowy, poświadczenia i listę modeli. Zapisuje się w konfiguracji OpenCode i działa w czacie jak każdy inny dostawca.',
'settings.providers.page.custom.field.providerID.label': 'ID dostawcy',
'settings.providers.page.custom.field.providerID.placeholder': 'moj-dostawca',
'settings.providers.page.custom.field.providerID.info': 'Małe litery, cyfry, myślniki i podkreślenia. Używane jako ID dostawcy OpenCode.',
'settings.providers.page.custom.field.name.label': 'Nazwa wyświetlana',
'settings.providers.page.custom.field.name.placeholder': 'Mój dostawca',
'settings.providers.page.custom.field.name.info': 'Widoczna w selektorach dostawcy i modelu.',
'settings.providers.page.custom.field.baseURL.label': 'Adres bazowy',
'settings.providers.page.custom.field.baseURL.placeholder': 'https://api.example.com/v1',
'settings.providers.page.custom.field.baseURL.info': 'Bazowy URL API zgodnego z OpenAI. Musi zaczynać się od http:// lub https://.',
'settings.providers.page.custom.field.apiKey.label': 'Klucz API',
'settings.providers.page.custom.field.apiKey.placeholder': 'sk-... lub {env:VAR_NAME}',
'settings.providers.page.custom.field.apiKey.info': 'Przechowywany w auth OpenCode, nie przez OpenChamber. Użyj {env:VAR_NAME}, aby odczytać klucz ze zmiennej środowiskowej.',
'settings.providers.page.custom.models.title': 'Modele',
'settings.providers.page.custom.models.idLabel': 'ID modelu',
'settings.providers.page.custom.models.idPlaceholder': 'gpt-4o',
'settings.providers.page.custom.models.nameLabel': 'Nazwa modelu',
'settings.providers.page.custom.models.namePlaceholder': 'GPT-4o',
'settings.providers.page.custom.models.add': 'Dodaj model',
'settings.providers.page.custom.models.remove': 'Usuń model',
'settings.providers.page.custom.headers.title': 'Nagłówki',
'settings.providers.page.custom.headers.description': 'Opcjonalne nagłówki żądania wysyłane przy każdym wywołaniu.',
'settings.providers.page.custom.headers.keyLabel': 'Nazwa nagłówka',
'settings.providers.page.custom.headers.keyPlaceholder': 'X-Custom-Header',
'settings.providers.page.custom.headers.valueLabel': 'Wartość nagłówka',
'settings.providers.page.custom.headers.valuePlaceholder': 'wartość',
'settings.providers.page.custom.headers.add': 'Dodaj nagłówek',
'settings.providers.page.custom.headers.remove': 'Usuń nagłówek',
'settings.providers.page.custom.actions.back': 'Wstecz',
'settings.providers.page.custom.actions.save': 'Zapisz dostawcę',
'settings.providers.page.custom.error.providerID.required': 'ID dostawcy jest wymagane',
'settings.providers.page.custom.error.providerID.format': 'Użyj małych liter, cyfr, myślników lub podkreśleń',
'settings.providers.page.custom.error.providerID.exists': 'Dostawca o tym ID jest już połączony',
'settings.providers.page.custom.error.name.required': 'Nazwa wyświetlana jest wymagana',
'settings.providers.page.custom.error.baseURL.required': 'Adres bazowy jest wymagany',
'settings.providers.page.custom.error.baseURL.format': 'Adres bazowy musi zaczynać się od http:// lub https://',
'settings.providers.page.custom.error.required': 'Wymagane',
'settings.providers.page.custom.error.duplicate': 'Duplikat',
'settings.providers.page.connect.noProvidersFound': 'Nie znaleziono dostawców',
'settings.providers.page.connect.providerField': 'Dostawca',
'settings.providers.page.connect.searchProvidersPlaceholder': 'Szukaj...',
@@ -1422,6 +1462,8 @@ export const settingsDict = {
'settings.providers.page.toast.oauthLinkCopyFailed': 'Nie udało się skopiować linku OAuth',
'settings.providers.page.toast.oauthStartFailed': 'Nie udało się rozpocząć procesu OAuth',
'settings.providers.page.toast.providerDisconnectFailed': 'Nie udało się odłączyć dostawcy',
'settings.providers.page.toast.customProviderSaved': 'Połączono {provider}',
'settings.providers.page.toast.customProviderSaveFailed': 'Nie udało się zapisać niestandardowego dostawcy',
'settings.providers.page.toast.providerDisconnected': 'Dostawca został odłączony',
'settings.providers.page.toast.providerSourcesLoadFailed': 'Nie udało się załadować źródeł dostawcy',
'settings.providers.sidebar.actions.connectProviderAria': 'Połącz dostawcę',