Features: - Welcome screen on first run (provider choice before LS starts) - 15+ AI providers (Google Gemini, OpenAI, Anthropic, DeepSeek, Ollama, etc.) - Provider config syncs to endpoints.json for translation proxy - Built-in Node.js translation proxy for non-native backends - Auto-update support, tray integration, URI scheme handler
359 lines
13 KiB
JavaScript
359 lines
13 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.ProviderService = exports.ProviderType = void 0;
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const os = require("os");
|
|
var ProviderType;
|
|
(function (ProviderType) {
|
|
ProviderType["GOOGLE_GEMINI"] = "google_gemini";
|
|
ProviderType["OPENAI"] = "openai";
|
|
ProviderType["ANTHROPIC"] = "anthropic";
|
|
ProviderType["Z_AI"] = "z_ai";
|
|
ProviderType["OPENCODE_ZEN"] = "opencode_zen";
|
|
ProviderType["OPENCODE_GO"] = "opencode_go";
|
|
ProviderType["OPENCODE_ZEN_ANTHROPIC"] = "opencode_zen_anthropic";
|
|
ProviderType["OPENCODE_GO_ANTHROPIC"] = "opencode_go_anthropic";
|
|
ProviderType["CROF_AI"] = "crof_ai";
|
|
ProviderType["NVIDIA_NIM"] = "nvidia_nim";
|
|
ProviderType["KILO_AI"] = "kilo_ai";
|
|
ProviderType["COMMAND_CODE"] = "command_code";
|
|
ProviderType["OPENROUTER"] = "openrouter";
|
|
ProviderType["OPENADAPTER"] = "openadapter";
|
|
ProviderType["DEEPSEEK"] = "deepseek";
|
|
ProviderType["OLLAMA"] = "ollama";
|
|
ProviderType["TOGETHER"] = "together";
|
|
ProviderType["GROQ"] = "groq";
|
|
ProviderType["CUSTOM"] = "custom";
|
|
})(ProviderType = exports.ProviderType || (exports.ProviderType = {}));
|
|
|
|
const PREDEFINED_PROVIDERS = {
|
|
[ProviderType.GOOGLE_GEMINI]: {
|
|
name: 'Google Gemini (OAuth)',
|
|
icon: '🔮',
|
|
description: 'Built-in Gemini via Google OAuth — zero config',
|
|
apiUrl: 'https://daily-cloudcode-pa.sandbox.googleapis.com',
|
|
backendType: 'gemini-native',
|
|
requiresApiKey: false,
|
|
apiKeyHint: '',
|
|
models: ['gemini-2.5-pro', 'gemini-2.5-flash', 'gemini-2.0-flash', 'gemini-3-flash-preview', 'gemini-3-pro-preview'],
|
|
defaultModel: 'gemini-2.5-pro',
|
|
},
|
|
[ProviderType.OPENAI]: {
|
|
name: 'OpenAI',
|
|
icon: '🟢',
|
|
description: 'GPT models via Responses API',
|
|
apiUrl: 'https://api.openai.com/v1',
|
|
backendType: 'native',
|
|
requiresApiKey: true,
|
|
apiKeyHint: 'OpenAI API Key (sk-...)',
|
|
models: ['gpt-4o', 'gpt-4o-mini', 'o1', 'o1-mini', 'o3', 'o3-mini'],
|
|
defaultModel: 'gpt-4o',
|
|
},
|
|
[ProviderType.ANTHROPIC]: {
|
|
name: 'Anthropic',
|
|
icon: '🟣',
|
|
description: 'Claude models via Messages API',
|
|
apiUrl: 'https://api.anthropic.com',
|
|
backendType: 'anthropic',
|
|
requiresApiKey: true,
|
|
apiKeyHint: 'Anthropic API Key (sk-ant-...)',
|
|
models: ['claude-sonnet-4-20250514', 'claude-3-5-sonnet-20241022', 'claude-3-5-haiku-20241022', 'claude-3-opus-20240229'],
|
|
defaultModel: 'claude-sonnet-4-20250514',
|
|
},
|
|
[ProviderType.Z_AI]: {
|
|
name: 'Z.AI Coding',
|
|
icon: '🅩',
|
|
description: 'GLM & Z models via Z.AI',
|
|
apiUrl: 'https://api.z.ai/api/coding/paas/v4',
|
|
backendType: 'openai-compat',
|
|
requiresApiKey: true,
|
|
apiKeyHint: 'Z.AI API Key',
|
|
models: ['glm-5.1', 'glm-4.7', 'GLM-4-Plus', 'GLM-4-Long', 'GLM-4-Flash', 'GLM-4-FlashX', 'GLM-Z1-Flash'],
|
|
defaultModel: 'glm-5.1',
|
|
},
|
|
[ProviderType.OPENCODE_ZEN]: {
|
|
name: 'OpenCode Zen',
|
|
icon: '🧘',
|
|
description: 'Multi-model via OpenCode Zen (OpenAI-compat)',
|
|
apiUrl: 'https://opencode.ai/zen/v1',
|
|
backendType: 'openai-compat',
|
|
requiresApiKey: true,
|
|
apiKeyHint: 'OpenCode API Key',
|
|
models: ['glm-5.1', 'glm-5', 'kimi-k2.5', 'kimi-k2.6', 'minimax-m2.7', 'minimax-m2.5', 'minimax-m2.5-free', 'deepseek-v4-flash-free', 'nemotron-3-super-free', 'qwen3.6-plus', 'qwen3.5-plus', 'big-pickle'],
|
|
defaultModel: 'glm-5.1',
|
|
},
|
|
[ProviderType.OPENCODE_GO]: {
|
|
name: 'OpenCode Go',
|
|
icon: '🚀',
|
|
description: 'Multi-model via OpenCode Go (OpenAI-compat)',
|
|
apiUrl: 'https://opencode.ai/zen/go/v1',
|
|
backendType: 'openai-compat',
|
|
requiresApiKey: true,
|
|
apiKeyHint: 'OpenCode API Key',
|
|
models: ['glm-5.1', 'glm-5', 'kimi-k2.5', 'kimi-k2.6', 'mimo-v2.5', 'mimo-v2.5-pro', 'minimax-m2.7', 'minimax-m2.5', 'qwen3.6-plus', 'qwen3.5-plus', 'deepseek-v4-pro', 'deepseek-v4-flash'],
|
|
defaultModel: 'glm-5.1',
|
|
},
|
|
[ProviderType.OPENCODE_ZEN_ANTHROPIC]: {
|
|
name: 'OpenCode Zen (Anthropic)',
|
|
icon: '🧘',
|
|
description: 'Claude models via OpenCode Zen',
|
|
apiUrl: 'https://opencode.ai/zen/v1',
|
|
backendType: 'anthropic',
|
|
requiresApiKey: true,
|
|
apiKeyHint: 'OpenCode API Key',
|
|
models: ['claude-opus-4-7', 'claude-opus-4-6', 'claude-opus-4-5', 'claude-opus-4-1', 'claude-sonnet-4-6', 'claude-sonnet-4-5', 'claude-sonnet-4', 'claude-haiku-4-5', 'claude-3-5-haiku'],
|
|
defaultModel: 'claude-sonnet-4-6',
|
|
},
|
|
[ProviderType.OPENCODE_GO_ANTHROPIC]: {
|
|
name: 'OpenCode Go (Anthropic)',
|
|
icon: '🚀',
|
|
description: 'Claude models via OpenCode Go',
|
|
apiUrl: 'https://opencode.ai/zen/go/v1',
|
|
backendType: 'anthropic',
|
|
requiresApiKey: true,
|
|
apiKeyHint: 'OpenCode API Key',
|
|
models: ['minimax-m2.7', 'minimax-m2.5'],
|
|
defaultModel: 'minimax-m2.7',
|
|
},
|
|
[ProviderType.CROF_AI]: {
|
|
name: 'Crof.ai',
|
|
icon: '🌐',
|
|
description: 'OpenAI-compatible models via Crof.ai',
|
|
apiUrl: 'https://crof.ai/v1',
|
|
backendType: 'openai-compat',
|
|
requiresApiKey: true,
|
|
apiKeyHint: 'Crof.ai API Key',
|
|
models: [],
|
|
defaultModel: '',
|
|
},
|
|
[ProviderType.NVIDIA_NIM]: {
|
|
name: 'NVIDIA NIM',
|
|
icon: '💚',
|
|
description: 'NVIDIA accelerated inference models',
|
|
apiUrl: 'https://integrate.api.nvidia.com/v1',
|
|
backendType: 'openai-compat',
|
|
requiresApiKey: true,
|
|
apiKeyHint: 'NVIDIA API Key (nvapi-...)',
|
|
models: [],
|
|
defaultModel: '',
|
|
},
|
|
[ProviderType.KILO_AI]: {
|
|
name: 'Kilo.ai Gateway',
|
|
icon: '⚖️',
|
|
description: 'Multi-provider via Kilo.ai',
|
|
apiUrl: 'https://api.kilo.ai/api/gateway',
|
|
backendType: 'openai-compat',
|
|
requiresApiKey: true,
|
|
apiKeyHint: 'Kilo.ai API Key',
|
|
models: [],
|
|
defaultModel: '',
|
|
},
|
|
[ProviderType.COMMAND_CODE]: {
|
|
name: 'Command Code',
|
|
icon: '⌘',
|
|
description: '20+ models via Command Code API',
|
|
apiUrl: 'https://api.commandcode.ai',
|
|
backendType: 'command-code',
|
|
requiresApiKey: true,
|
|
apiKeyHint: 'Command Code API Key',
|
|
ccVersion: '0.26.8',
|
|
models: [
|
|
'deepseek/deepseek-v4-flash', 'deepseek/deepseek-v4-pro',
|
|
'anthropic:claude-sonnet-4-6', 'anthropic:claude-haiku-4-5-20251001',
|
|
'anthropic:claude-opus-4-7', 'anthropic:claude-opus-4-6',
|
|
'openai:gpt-5.5', 'openai:gpt-5.4', 'openai:gpt-5.4-mini', 'openai:gpt-5.3-codex',
|
|
'moonshotai/Kimi-K2.6', 'moonshotai/Kimi-K2.5',
|
|
'zai-org/GLM-5.1', 'zai-org/GLM-5',
|
|
'MiniMaxAI/MiniMax-M2.7', 'MiniMaxAI/MiniMax-M2.5',
|
|
'Qwen/Qwen3.6-Max-Preview', 'Qwen/Qwen3.6-Plus',
|
|
'stepfun/Step-3.5-Flash', 'google/gemini-3.1-flash-lite',
|
|
],
|
|
defaultModel: 'deepseek/deepseek-v4-flash',
|
|
},
|
|
[ProviderType.OPENROUTER]: {
|
|
name: 'OpenRouter',
|
|
icon: '🔀',
|
|
description: 'Route to hundreds of models via OpenRouter',
|
|
apiUrl: 'https://openrouter.ai/api/v1',
|
|
backendType: 'openai-compat',
|
|
requiresApiKey: true,
|
|
apiKeyHint: 'OpenRouter API Key (sk-or-...)',
|
|
models: [],
|
|
defaultModel: '',
|
|
},
|
|
[ProviderType.OPENADAPTER]: {
|
|
name: 'OpenAdapter',
|
|
icon: '🔌',
|
|
description: 'Free/proxy models via OpenAdapter',
|
|
apiUrl: 'https://api.openadapter.in/v1',
|
|
backendType: 'openai-compat',
|
|
requiresApiKey: true,
|
|
apiKeyHint: 'OpenAdapter API Key',
|
|
models: ['0G-DeepSeek-V3', '0G-DeepSeek-v4-Pro', '0G-GLM-5', '0G-GLM-5.1', '0G-Qwen3.6', '0G-Qwen-VL'],
|
|
defaultModel: '0G-DeepSeek-v4-Pro',
|
|
},
|
|
[ProviderType.DEEPSEEK]: {
|
|
name: 'DeepSeek',
|
|
icon: '🔍',
|
|
description: 'DeepSeek models directly',
|
|
apiUrl: 'https://api.deepseek.com/v1',
|
|
backendType: 'openai-compat',
|
|
requiresApiKey: true,
|
|
apiKeyHint: 'DeepSeek API Key',
|
|
models: ['deepseek-chat', 'deepseek-reasoner'],
|
|
defaultModel: 'deepseek-chat',
|
|
},
|
|
[ProviderType.OLLAMA]: {
|
|
name: 'Ollama (Local)',
|
|
icon: '🦙',
|
|
description: 'Run models locally with Ollama',
|
|
apiUrl: 'http://127.0.0.1:11434',
|
|
backendType: 'openai-compat',
|
|
requiresApiKey: false,
|
|
apiKeyHint: '',
|
|
models: ['llama3.1', 'llama3', 'codellama', 'mistral', 'mixtral', 'deepseek-coder', 'qwen2.5-coder'],
|
|
defaultModel: 'llama3.1',
|
|
},
|
|
[ProviderType.TOGETHER]: {
|
|
name: 'Together AI',
|
|
icon: '🤝',
|
|
description: 'Open-source models via Together',
|
|
apiUrl: 'https://api.together.xyz/v1',
|
|
backendType: 'openai-compat',
|
|
requiresApiKey: true,
|
|
apiKeyHint: 'Together API Key',
|
|
models: [],
|
|
defaultModel: '',
|
|
},
|
|
[ProviderType.GROQ]: {
|
|
name: 'Groq',
|
|
icon: '⚡',
|
|
description: 'Ultra-fast inference via Groq',
|
|
apiUrl: 'https://api.groq.com/openai/v1',
|
|
backendType: 'openai-compat',
|
|
requiresApiKey: true,
|
|
apiKeyHint: 'Groq API Key (gsk_...)',
|
|
models: [],
|
|
defaultModel: '',
|
|
},
|
|
[ProviderType.CUSTOM]: {
|
|
name: 'Custom Provider',
|
|
icon: '⚙️',
|
|
description: 'Any OpenAI-compatible endpoint',
|
|
apiUrl: '',
|
|
backendType: 'openai-compat',
|
|
requiresApiKey: false,
|
|
apiKeyHint: 'API Key (if required)',
|
|
models: [],
|
|
defaultModel: '',
|
|
},
|
|
};
|
|
|
|
class ProviderService {
|
|
constructor(storageManager) {
|
|
this.storageManager = storageManager;
|
|
this.configPath = path.join(os.homedir(), '.ag-x', 'ag-x', 'provider_config.json');
|
|
this.config = this.loadConfig();
|
|
}
|
|
loadConfig() {
|
|
try {
|
|
if (fs.existsSync(this.configPath)) {
|
|
const content = fs.readFileSync(this.configPath, 'utf-8');
|
|
return JSON.parse(content);
|
|
}
|
|
}
|
|
catch (e) {
|
|
console.error('Error loading provider config:', e);
|
|
}
|
|
// Build defaults from all predefined providers
|
|
const providers = {};
|
|
for (const [key, preset] of Object.entries(PREDEFINED_PROVIDERS)) {
|
|
providers[key] = {
|
|
...preset,
|
|
apiKey: '',
|
|
model: preset.defaultModel,
|
|
enabled: key === ProviderType.GOOGLE_GEMINI,
|
|
};
|
|
}
|
|
return {
|
|
activeProvider: ProviderType.GOOGLE_GEMINI,
|
|
providers,
|
|
};
|
|
}
|
|
saveConfig() {
|
|
try {
|
|
const dir = path.dirname(this.configPath);
|
|
if (!fs.existsSync(dir)) {
|
|
fs.mkdirSync(dir, { recursive: true });
|
|
}
|
|
fs.writeFileSync(this.configPath, JSON.stringify(this.config, null, 2), 'utf-8');
|
|
}
|
|
catch (e) {
|
|
console.error('Error saving provider config:', e);
|
|
}
|
|
}
|
|
getActiveProvider() {
|
|
return this.config.activeProvider;
|
|
}
|
|
setActiveProvider(providerType) {
|
|
this.config.activeProvider = providerType;
|
|
this.saveConfig();
|
|
}
|
|
getProviderConfig(providerType) {
|
|
return this.config.providers[providerType] || null;
|
|
}
|
|
updateProviderConfig(providerType, updates) {
|
|
if (this.config.providers[providerType]) {
|
|
Object.assign(this.config.providers[providerType], updates);
|
|
this.saveConfig();
|
|
}
|
|
}
|
|
getAllProviders() {
|
|
return this.config.providers;
|
|
}
|
|
getPredefinedProviders() {
|
|
return PREDEFINED_PROVIDERS;
|
|
}
|
|
getActiveProviderApiUrl() {
|
|
const provider = this.config.providers[this.config.activeProvider];
|
|
return provider?.apiUrl || 'https://daily-cloudcode-pa.sandbox.googleapis.com';
|
|
}
|
|
getActiveProviderApiKey() {
|
|
const provider = this.config.providers[this.config.activeProvider];
|
|
return provider?.apiKey || '';
|
|
}
|
|
getActiveProviderModel() {
|
|
const provider = this.config.providers[this.config.activeProvider];
|
|
return provider?.model || '';
|
|
}
|
|
getActiveBackendType() {
|
|
const provider = this.config.providers[this.config.activeProvider];
|
|
return provider?.backendType || 'gemini-native';
|
|
}
|
|
getActiveCcVersion() {
|
|
const provider = this.config.providers[this.config.activeProvider];
|
|
return provider?.ccVersion || '0.26.8';
|
|
}
|
|
/**
|
|
* Does this provider need the built-in translation proxy?
|
|
* gemini-native and native (OpenAI) go direct; everything else needs proxy.
|
|
*/
|
|
needsProxy() {
|
|
const bt = this.getActiveBackendType();
|
|
return bt !== 'gemini-native' && bt !== 'native';
|
|
}
|
|
/**
|
|
* Does this provider need the full translation proxy (anthropic, command-code)?
|
|
* command-code and anthropic need the full Python proxy for full compatibility.
|
|
*/
|
|
needsTranslateProxy() {
|
|
const bt = this.getActiveBackendType();
|
|
return bt === 'command-code' || bt === 'anthropic';
|
|
}
|
|
getProxyPort() {
|
|
return 9876;
|
|
}
|
|
}
|
|
exports.ProviderService = ProviderService;
|