Complete source code - AI Provider Edition v2.0.1

Added complete source code and pre-compiled application:

Source Code:
- app.asar (compiled Electron app)
- app-extracted/ (all extracted source files)
  - dist/services/aiProviderService.js
  - dist/services/settingsService.js
  - dist/ipcHandlers.js
  - dist/aiProviderAPI.ts
  - dist/ai-provider-settings.html
  - And all other application files

Build Tools:
- scripts/extract-app.sh
- scripts/repack-app.sh
- scripts/build-deb.sh
- scripts/install-deb.sh

Documentation:
- SOURCE_CODE.md (repository structure)
- BUILD.md (build instructions)
- README.md (overview)
- docs/ (complete API docs)
  - AI_PROVIDER_SPECIFICATION.md
  - AI_PROVIDER_README.md
  - IMPLEMENTATION_SUMMARY.md

Features included:
- 17+ AI provider presets
- One-click provider setup
- Model auto-fetching
- Connection testing
- Modern GUI interface
- Complete IPC handlers (14 new)
- TypeScript API wrapper
- Persistent settings

Ready to build and customize!
This commit is contained in:
2026-05-22 10:34:10 +00:00
Unverified
parent e6b6405912
commit 0265d58123
55 changed files with 9030 additions and 210 deletions

View File

@@ -0,0 +1,519 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AIProviderService = exports.AIProviderType = exports.AIProviderCapability = void 0;
var AIProviderCapability;
(function (AIProviderCapability) {
AIProviderCapability["CHAT"] = "chat";
AIProviderCapability["COMPLETION"] = "completion";
AIProviderCapability["EMBEDDING"] = "embedding";
AIProviderCapability["VISION"] = "vision";
AIProviderCapability["TOOL_USE"] = "tool_use";
AIProviderCapability["STREAMING"] = "streaming";
})(AIProviderCapability = exports.AIProviderCapability || (exports.AIProviderCapability = {}));
var AIProviderType;
(function (AIProviderType) {
AIProviderType["OPENAI"] = "openai";
AIProviderType["ANTHROPIC"] = "anthropic";
AIProviderType["OLLAMA"] = "ollama";
AIProviderType["GROQ"] = "groq";
AIProviderType["OPENROUTER"] = "openrouter";
AIProviderType["CUSTOM"] = "custom";
AIProviderType["GOOGLE_GEMINI"] = "google_gemini";
AIProviderType["COMMAND_CODE"] = "command_code";
AIProviderType["NVIDIA_NIM"] = "nvidia_nim";
AIProviderType["CROF_AI"] = "crof_ai";
AIProviderType["KILO_AI"] = "kilo_ai";
AIProviderType["OPEN_ADAPTER"] = "open_adapter";
AIProviderType["Z_AI"] = "z_ai";
AIProviderType["GOOGLE_ANTIGRAVITY"] = "google_antigravity";
})(AIProviderType = exports.AIProviderType || (exports.AIProviderType = {}));
const PROVIDER_PRESETS = {
"Custom": {
type: AIProviderType.CUSTOM,
endpoint: "",
capabilities: [AIProviderCapability.CHAT, AIProviderCapability.STREAMING],
},
"OpenAI": {
type: AIProviderType.OPENAI,
endpoint: "https://api.openai.com/v1",
models: ["gpt-4o", "gpt-4o-mini", "gpt-4-turbo", "gpt-3.5-turbo"],
capabilities: [AIProviderCapability.CHAT, AIProviderCapability.COMPLETION, AIProviderCapability.EMBEDDING, AIProviderCapability.VISION, AIProviderCapability.TOOL_USE, AIProviderCapability.STREAMING],
},
"Anthropic": {
type: AIProviderType.ANTHROPIC,
endpoint: "https://api.anthropic.com/v1",
models: ["claude-sonnet-4-20250514", "claude-3-5-sonnet-latest", "claude-3-opus-latest", "claude-3-haiku-latest"],
capabilities: [AIProviderCapability.CHAT, AIProviderCapability.VISION, AIProviderCapability.TOOL_USE, AIProviderCapability.STREAMING],
},
"OpenCode Zen (OpenAI-compatible)": {
type: AIProviderType.CUSTOM,
endpoint: "https://opencode.ai/zen/v1",
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",
],
capabilities: [AIProviderCapability.CHAT, AIProviderCapability.COMPLETION, AIProviderCapability.STREAMING],
},
"OpenCode Zen (Anthropic)": {
type: AIProviderType.ANTHROPIC,
endpoint: "https://opencode.ai/zen/v1",
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",
],
capabilities: [AIProviderCapability.CHAT, AIProviderCapability.VISION, AIProviderCapability.TOOL_USE, AIProviderCapability.STREAMING],
},
"OpenCode Go (OpenAI-compatible)": {
type: AIProviderType.CUSTOM,
endpoint: "https://opencode.ai/zen/go/v1",
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",
],
capabilities: [AIProviderCapability.CHAT, AIProviderCapability.COMPLETION, AIProviderCapability.STREAMING],
},
"OpenCode Go (Anthropic)": {
type: AIProviderType.ANTHROPIC,
endpoint: "https://opencode.ai/zen/go/v1",
models: ["minimax-m2.7", "minimax-m2.5"],
capabilities: [AIProviderCapability.CHAT, AIProviderCapability.VISION, AIProviderCapability.TOOL_USE, AIProviderCapability.STREAMING],
},
"Crof.ai": {
type: AIProviderType.CUSTOM,
endpoint: "https://crof.ai/v1",
models: [],
capabilities: [AIProviderCapability.CHAT, AIProviderCapability.COMPLETION, AIProviderCapability.STREAMING],
},
"NVIDIA NIM": {
type: AIProviderType.NVIDIA_NIM,
endpoint: "https://integrate.api.nvidia.com/v1",
models: [],
capabilities: [AIProviderCapability.CHAT, AIProviderCapability.COMPLETION, AIProviderCapability.STREAMING],
},
"Kilo.ai Gateway": {
type: AIProviderType.KILO_AI,
endpoint: "https://api.kilo.ai/api/gateway",
models: [],
capabilities: [AIProviderCapability.CHAT, AIProviderCapability.COMPLETION, AIProviderCapability.STREAMING],
},
"Command Code": {
type: AIProviderType.COMMAND_CODE,
endpoint: "https://api.commandcode.ai",
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",
],
capabilities: [AIProviderCapability.CHAT, AIProviderCapability.TOOL_USE, AIProviderCapability.STREAMING],
},
"OpenRouter": {
type: AIProviderType.OPENROUTER,
endpoint: "https://openrouter.ai/api/v1",
models: [],
capabilities: [AIProviderCapability.CHAT, AIProviderCapability.COMPLETION, AIProviderCapability.STREAMING],
},
"Google Gemini (API Key)": {
type: AIProviderType.GOOGLE_GEMINI,
endpoint: "https://generativelanguage.googleapis.com/v1beta/openai",
models: [
"gemini-2.5-flash", "gemini-2.5-pro",
"gemini-2.0-flash", "gemini-2.0-flash-lite",
"gemini-2.5-flash-preview-native-audio-dialog",
],
capabilities: [AIProviderCapability.CHAT, AIProviderCapability.VISION, AIProviderCapability.STREAMING],
},
"Google Gemini (OAuth)": {
type: AIProviderType.GOOGLE_GEMINI,
endpoint: "https://cloudcode-pa.googleapis.com",
models: ["gemini-2.5-flash", "gemini-2.5-pro"],
capabilities: [AIProviderCapability.CHAT, AIProviderCapability.VISION, AIProviderCapability.TOOL_USE, AIProviderCapability.STREAMING],
},
"Google Antigravity (OAuth)": {
type: AIProviderType.GOOGLE_ANTIGRAVITY,
endpoint: "https://daily-cloudcode-pa.sandbox.googleapis.com",
models: [
"antigravity-gemini-3-flash",
"antigravity-gemini-3-pro",
"antigravity-gemini-3.1-pro",
"antigravity-claude-sonnet-4-6",
"antigravity-claude-opus-4-6-thinking",
"gemini-2.5-flash", "gemini-2.5-pro",
"gemini-3-flash-preview", "gemini-3-pro-preview", "gemini-3.1-pro-preview",
],
capabilities: [AIProviderCapability.CHAT, AIProviderCapability.VISION, AIProviderCapability.TOOL_USE, AIProviderCapability.STREAMING],
},
"OpenAdapter": {
type: AIProviderType.OPEN_ADAPTER,
endpoint: "https://api.openadapter.in/v1",
models: [
"0G-DeepSeek-V3",
"0G-DeepSeek-v4-Pro",
"0G-GLM-5",
"0G-GLM-5.1",
"0G-Qwen3.6",
"0G-Qwen-VL",
],
capabilities: [AIProviderCapability.CHAT, AIProviderCapability.COMPLETION, AIProviderCapability.STREAMING],
},
"Z.ai Coding": {
type: AIProviderType.Z_AI,
endpoint: "https://api.z.ai/api/coding/paas/v4",
models: [
"glm-5.1", "glm-4.7", "GLM-4-Plus", "GLM-4-Long",
"GLM-4-Flash", "GLM-4-FlashX", "GLM-Z1-Flash",
],
capabilities: [AIProviderCapability.CHAT, AIProviderCapability.COMPLETION, AIProviderCapability.STREAMING],
},
"Ollama (Local)": {
type: AIProviderType.OLLAMA,
endpoint: "http://localhost:11434/v1",
apiKey: "ollama",
models: ["llama3", "codellama", "mistral", "neural-chat"],
capabilities: [AIProviderCapability.CHAT, AIProviderCapability.COMPLETION, AIProviderCapability.STREAMING],
},
"Groq": {
type: AIProviderType.GROQ,
endpoint: "https://api.groq.com/openai/v1",
models: ["llama-3.1-8b-instant", "llama-3.1-70b-versatile", "mixtral-8x7b-32768"],
capabilities: [AIProviderCapability.CHAT, AIProviderCapability.COMPLETION, AIProviderCapability.STREAMING],
},
};
const DEFAULT_PROVIDERS = [
{
id: 'openai-default',
name: 'OpenAI',
type: AIProviderType.OPENAI,
endpoint: 'https://api.openai.com/v1',
apiKey: '',
models: ['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'gpt-3.5-turbo'],
defaultModel: 'gpt-4o',
capabilities: [
AIProviderCapability.CHAT,
AIProviderCapability.COMPLETION,
AIProviderCapability.EMBEDDING,
AIProviderCapability.VISION,
AIProviderCapability.TOOL_USE,
AIProviderCapability.STREAMING,
],
isEnabled: true,
isDefault: true,
},
{
id: 'anthropic-default',
name: 'Anthropic',
type: AIProviderType.ANTHROPIC,
endpoint: 'https://api.anthropic.com/v1',
apiKey: '',
models: ['claude-sonnet-4-20250514', 'claude-3-5-sonnet-latest', 'claude-3-opus-latest', 'claude-3-haiku-latest'],
defaultModel: 'claude-sonnet-4-20250514',
capabilities: [
AIProviderCapability.CHAT,
AIProviderCapability.VISION,
AIProviderCapability.TOOL_USE,
AIProviderCapability.STREAMING,
],
isEnabled: true,
isDefault: false,
},
{
id: 'ollama-default',
name: 'Ollama (Local)',
type: AIProviderType.OLLAMA,
endpoint: 'http://localhost:11434/v1',
apiKey: 'ollama',
models: ['llama3', 'codellama', 'mistral', 'neural-chat'],
defaultModel: 'llama3',
capabilities: [
AIProviderCapability.CHAT,
AIProviderCapability.COMPLETION,
AIProviderCapability.STREAMING,
],
isEnabled: true,
isDefault: false,
},
];
class AIProviderService {
constructor(storageManager) {
this.storageManager = storageManager;
this.providers = new Map();
this.eventEmitter = new (require('events').EventEmitter)();
}
async initialize() {
const items = await this.storageManager.getItems();
const storedProviders = items['aiProviders'];
if (storedProviders) {
try {
const providersArray = JSON.parse(storedProviders);
providersArray.forEach((provider) => {
this.providers.set(provider.id, provider);
});
} catch (e) {
console.error('Error loading AI providers:', e);
this.loadDefaultProviders();
}
} else {
this.loadDefaultProviders();
}
}
loadDefaultProviders() {
DEFAULT_PROVIDERS.forEach((provider) => {
this.providers.set(provider.id, { ...provider });
});
this.persistProviders();
}
async persistProviders() {
const providersArray = Array.from(this.providers.values());
await this.storageManager.updateItems({
'aiProviders': JSON.stringify(providersArray),
});
}
getAllProviders() {
return Array.from(this.providers.values());
}
getProvider(id) {
return this.providers.get(id);
}
getEnabledProviders() {
return this.getAllProviders().filter(p => p.isEnabled);
}
getDefaultProvider() {
return this.getAllProviders().find(p => p.isDefault) || this.getAllProviders()[0];
}
getAvailablePresets() {
return Object.keys(PROVIDER_PRESETS);
}
getPreset(presetName) {
return PROVIDER_PRESETS[presetName];
}
async addProvider(provider) {
const newProvider = {
id: `custom-${Date.now()}`,
name: provider.name || 'Custom Provider',
type: provider.type || AIProviderType.CUSTOM,
endpoint: provider.endpoint || '',
apiKey: provider.apiKey || '',
models: provider.models || [],
defaultModel: provider.defaultModel || (provider.models && provider.models[0]) || '',
capabilities: provider.capabilities || [AIProviderCapability.CHAT],
isEnabled: true,
isDefault: false,
...provider,
};
this.providers.set(newProvider.id, newProvider);
await this.persistProviders();
this.emit('provider-added', newProvider);
return newProvider;
}
async addProviderFromPreset(presetName, apiKey = '') {
const preset = PROVIDER_PRESETS[presetName];
if (!preset) {
throw new Error(`Preset "${presetName}" not found`);
}
const normalizedName = presetName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
const newProvider = {
id: `${normalizedName}-${Date.now()}`,
name: presetName,
type: preset.type || AIProviderType.CUSTOM,
endpoint: preset.endpoint || '',
apiKey: apiKey || preset.apiKey || '',
models: preset.models || [],
defaultModel: preset.models && preset.models[0] || '',
capabilities: preset.capabilities || [AIProviderCapability.CHAT, AIProviderCapability.STREAMING],
isEnabled: true,
isDefault: false,
presetName: presetName,
};
this.providers.set(newProvider.id, newProvider);
await this.persistProviders();
this.emit('provider-added', newProvider);
return newProvider;
}
async updateProvider(id, updates) {
const provider = this.providers.get(id);
if (!provider) {
throw new Error(`Provider with id ${id} not found`);
}
const updatedProvider = {
...provider,
...updates,
id: provider.id,
};
this.providers.set(id, updatedProvider);
await this.persistProviders();
this.emit('provider-updated', updatedProvider);
return updatedProvider;
}
async deleteProvider(id) {
const provider = this.providers.get(id);
if (!provider) {
throw new Error(`Provider with id ${id} not found`);
}
this.providers.delete(id);
await this.persistProviders();
this.emit('provider-deleted', id);
}
async setDefaultProvider(id) {
const provider = this.providers.get(id);
if (!provider) {
throw new Error(`Provider with id ${id} not found`);
}
this.getAllProviders().forEach((p) => {
p.isDefault = p.id === id;
});
await this.persistProviders();
this.emit('default-provider-changed', id);
}
async toggleProvider(id, enabled) {
const provider = this.providers.get(id);
if (!provider) {
throw new Error(`Provider with id ${id} not found`);
}
provider.isEnabled = enabled;
await this.persistProviders();
this.emit('provider-toggled', { id, enabled });
}
async testConnection(id) {
const provider = this.providers.get(id);
if (!provider) {
throw new Error(`Provider with id ${id} not found`);
}
if (!provider.endpoint) {
return {
success: false,
status: -1,
message: 'No endpoint configured',
};
}
try {
const headers = {
'Content-Type': 'application/json',
};
if (provider.apiKey && provider.apiKey !== 'ollama') {
headers['Authorization'] = `Bearer ${provider.apiKey}`;
}
const response = await fetch(`${provider.endpoint}/models`, {
method: 'GET',
headers: headers,
});
return {
success: response.ok,
status: response.status,
message: response.ok ? 'Connection successful' : `Error: ${response.statusText}`,
};
} catch (error) {
return {
success: false,
status: -1,
message: `Connection failed: ${error.message}`,
};
}
}
async fetchModels(id) {
const provider = this.providers.get(id);
if (!provider) {
throw new Error(`Provider with id ${id} not found`);
}
if (!provider.endpoint) {
throw new Error('No endpoint configured');
}
try {
const headers = {
'Content-Type': 'application/json',
};
if (provider.apiKey && provider.apiKey !== 'ollama') {
headers['Authorization'] = `Bearer ${provider.apiKey}`;
}
const response = await fetch(`${provider.endpoint}/models`, {
method: 'GET',
headers: headers,
});
if (!response.ok) {
throw new Error(`API error: ${response.statusText}`);
}
const data = await response.json();
let models = [];
if (data.data && Array.isArray(data.data)) {
models = data.data.map(model => model.id || model.model || model.name).filter(Boolean);
} else if (data.models && Array.isArray(data.models)) {
models = data.models.map(model => model.id || model.model || model.name).filter(Boolean);
}
if (models.length > 0) {
provider.models = models;
provider.defaultModel = models[0];
await this.persistProviders();
this.emit('provider-updated', provider);
}
return models;
} catch (error) {
throw new Error(`Failed to fetch models: ${error.message}`);
}
}
on(event, listener) {
this.eventEmitter.on(event, listener);
}
off(event, listener) {
this.eventEmitter.off(event, listener);
}
emit(event, ...args) {
this.eventEmitter.emit(event, ...args);
}
}
exports.AIProviderService = AIProviderService;