UNPKG

@nanocollective/nanocoder

Version:

A local-first CLI coding agent that brings the power of agentic coding tools like Claude Code and Gemini CLI to local models or controlled APIs like OpenRouter

552 lines 16.4 kB
const urlValidator = (value) => { if (!value) return undefined; try { const url = new URL(value); // Check protocol - allow both HTTP and HTTPS // Users may have legitimate reasons for HTTP (VPNs, internal networks, // Ollama which doesn't use API keys, etc.) if (!['http:', 'https:'].includes(url.protocol)) { return 'URL must use http or https protocol'; } return undefined; } catch { return 'Invalid URL format'; } }; export const PROVIDER_TEMPLATES = [ { id: 'ollama', name: 'Ollama', modelsEndpoint: 'ollama', fields: [ { name: 'providerName', prompt: 'Provider name', default: 'ollama', required: true, }, { name: 'baseUrl', prompt: 'Base URL', default: 'http://localhost:11434/v1', validator: urlValidator, }, { name: 'model', prompt: 'Model name(s) (comma-separated)', default: '', required: true, }, ], buildConfig: answers => ({ name: answers.providerName || 'ollama', baseUrl: answers.baseUrl || 'http://localhost:11434/v1', models: answers.model .split(',') .map(m => m.trim()) .filter(Boolean), }), }, { id: 'llama-cpp', name: 'llama.cpp server', modelsEndpoint: 'openai-compatible', fields: [ { name: 'providerName', prompt: 'Provider name', default: 'llama-cpp', required: true, }, { name: 'baseUrl', prompt: 'Base URL', default: 'http://localhost:8080/v1', validator: urlValidator, }, { name: 'model', prompt: 'Model name(s) (comma-separated)', default: '', required: true, }, ], buildConfig: answers => ({ name: answers.providerName || 'llama-cpp', baseUrl: answers.baseUrl || 'http://localhost:8080/v1', models: answers.model .split(',') .map(m => m.trim()) .filter(Boolean), }), }, { id: 'lmstudio', name: 'LM Studio', modelsEndpoint: 'openai-compatible', fields: [ { name: 'providerName', prompt: 'Provider name', default: 'lmstudio', required: true, }, { name: 'baseUrl', prompt: 'Base URL', default: 'http://localhost:1234/v1', validator: urlValidator, }, { name: 'model', prompt: 'Model name(s) (comma-separated)', default: '', required: true, }, ], buildConfig: answers => ({ name: answers.providerName || 'lmstudio', baseUrl: answers.baseUrl || 'http://localhost:1234/v1', models: answers.model .split(',') .map(m => m.trim()) .filter(Boolean), }), }, { id: 'gemini', name: 'Google Gemini', fields: [ { name: 'apiKey', prompt: 'API Key (from https://aistudio.google.com/apikey)', required: true, sensitive: true, }, { name: 'model', prompt: 'Model name(s) (comma-separated)', default: 'gemini-3-flash-preview, gemini-3-pro-preview', required: true, }, { name: 'providerName', prompt: 'Provider name', default: 'Gemini', }, ], buildConfig: answers => ({ name: answers.providerName || 'Gemini', sdkProvider: 'google', baseUrl: 'https://generativelanguage.googleapis.com/v1beta', apiKey: answers.apiKey, models: answers.model .split(',') .map(m => m.trim()) .filter(Boolean), }), }, { id: 'openrouter', name: 'OpenRouter', fields: [ { name: 'apiKey', prompt: 'API Key', required: true, sensitive: true, }, { name: 'model', prompt: 'Model name(s) (comma-separated)', default: '', required: true, }, { name: 'providerName', prompt: 'Provider name', default: 'OpenRouter', }, ], buildConfig: answers => ({ name: answers.providerName || 'OpenRouter', baseUrl: 'https://openrouter.ai/api/v1', apiKey: answers.apiKey, models: answers.model .split(',') .map(m => m.trim()) .filter(Boolean), }), }, { id: 'openai', name: 'OpenAI', modelsEndpoint: 'openai', fields: [ { name: 'apiKey', prompt: 'API Key', required: true, sensitive: true, }, { name: 'model', prompt: 'Model name(s) (comma-separated)', default: '', required: true, }, { name: 'organizationId', prompt: 'Organization ID (optional)', required: false, }, { name: 'providerName', prompt: 'Provider name', default: 'openai', }, ], buildConfig: answers => { const config = { name: answers.providerName || 'openai', baseUrl: 'https://api.openai.com/v1', apiKey: answers.apiKey, models: answers.model .split(',') .map(m => m.trim()) .filter(Boolean), }; if (answers.organizationId) { config.organizationId = answers.organizationId; } return config; }, }, { id: 'anthropic', name: 'Anthropic Claude', modelsEndpoint: 'anthropic', fields: [ { name: 'apiKey', prompt: 'API Key', required: true, sensitive: true, }, { name: 'model', prompt: 'Model name(s) (comma-separated)', default: '', required: true, }, { name: 'providerName', prompt: 'Provider name', default: 'anthropic', }, ], buildConfig: answers => ({ name: answers.providerName || 'anthropic', sdkProvider: 'anthropic', baseUrl: 'https://api.anthropic.com/v1', apiKey: answers.apiKey, models: answers.model .split(',') .map(m => m.trim()) .filter(Boolean), }), }, { id: 'mistral', name: 'Mistral AI', modelsEndpoint: 'mistral', fields: [ { name: 'apiKey', prompt: 'API Key', required: true, sensitive: true, }, { name: 'model', prompt: 'Model name(s) (comma-separated)', default: '', required: true, }, { name: 'providerName', prompt: 'Provider name', default: 'Mistral', }, ], buildConfig: answers => ({ name: answers.providerName || 'Mistral', baseUrl: 'https://api.mistral.ai/v1', apiKey: answers.apiKey, models: answers.model .split(',') .map(m => m.trim()) .filter(Boolean), }), }, { id: 'z-ai', name: 'Z.ai', fields: [ { name: 'providerName', prompt: 'Provider name', default: 'Z.ai', required: true, }, { name: 'apiKey', prompt: 'API Key', required: true, sensitive: true, }, { name: 'model', prompt: 'Model name(s) (comma-separated)', default: 'glm-5, glm-4.7, glm-4.7-flash', required: true, }, ], buildConfig: answers => ({ name: answers.providerName || 'Z.ai', baseUrl: 'https://api.z.ai/api/paas/v4/', apiKey: answers.apiKey, models: answers.model .split(',') .map(m => m.trim()) .filter(Boolean), }), }, { id: 'z-ai-coding', name: 'Z.ai Coding Subscription', fields: [ { name: 'providerName', prompt: 'Provider name', default: 'Z.ai Coding Subscription', required: true, }, { name: 'apiKey', prompt: 'API Key', required: true, sensitive: true, }, { name: 'model', prompt: 'Model name(s) (comma-separated)', default: 'glm-5, glm-4.7, glm-4.7-flash', required: true, }, ], buildConfig: answers => ({ name: answers.providerName || 'Z.ai Coding Subscription', baseUrl: 'https://api.z.ai/api/coding/paas/v4/', apiKey: answers.apiKey, models: answers.model .split(',') .map(m => m.trim()) .filter(Boolean), }), }, { id: 'github-models', name: 'GitHub Models', modelsEndpoint: 'github', fields: [ { name: 'apiKey', prompt: 'GitHub Token (PAT with models:read scope)', required: true, sensitive: true, }, { name: 'model', prompt: 'Model name(s) (comma-separated)', default: '', required: true, }, { name: 'providerName', prompt: 'Provider name', default: 'GitHub Models', }, ], buildConfig: answers => ({ name: answers.providerName || 'GitHub Models', baseUrl: 'https://models.github.ai/inference', apiKey: answers.apiKey, models: answers.model .split(',') .map(m => m.trim()) .filter(Boolean), }), }, { id: 'kimi-code', name: 'Kimi Code', fields: [ { name: 'apiKey', prompt: 'API Key', required: true, sensitive: true, }, { name: 'model', prompt: 'Model name(s) (comma-separated)', default: 'kimi-for-coding', required: true, }, { name: 'providerName', prompt: 'Provider name', default: 'Kimi Code', }, ], buildConfig: answers => ({ name: answers.providerName || 'Kimi Code', sdkProvider: 'anthropic', baseUrl: 'https://api.kimi.com/coding/v1', apiKey: answers.apiKey, models: answers.model .split(',') .map(m => m.trim()) .filter(Boolean), }), }, { id: 'minimax-coding', name: 'MiniMax Coding Plan', fields: [ { name: 'apiKey', prompt: 'API Key', required: true, sensitive: true, }, { name: 'model', prompt: 'Model name(s) (comma-separated)', default: 'MiniMax-M2.5', required: true, }, { name: 'providerName', prompt: 'Provider name', default: 'MiniMax Coding', }, ], buildConfig: answers => ({ name: answers.providerName || 'MiniMax Coding', sdkProvider: 'anthropic', baseUrl: 'https://api.minimax.io/anthropic/v1', apiKey: answers.apiKey, models: answers.model .split(',') .map(m => m.trim()) .filter(Boolean), }), }, { id: 'poe', name: 'Poe', fields: [ { name: 'apiKey', prompt: 'API Key (from poe.com/api_key)', required: true, sensitive: true, }, { name: 'model', prompt: 'Model name(s) (comma-separated)', default: '', required: true, }, { name: 'providerName', prompt: 'Provider name', default: 'Poe', }, ], buildConfig: answers => ({ name: answers.providerName || 'Poe', baseUrl: 'https://api.poe.com/v1', apiKey: answers.apiKey, models: answers.model .split(',') .map(m => m.trim()) .filter(Boolean), }), }, { id: 'custom', name: 'Custom Provider', modelsEndpoint: 'openai-compatible', fields: [ { name: 'providerName', prompt: 'Provider name', required: true, }, { name: 'baseUrl', prompt: 'Base URL', required: true, validator: urlValidator, }, { name: 'apiKey', prompt: 'API Key (optional)', required: false, sensitive: true, }, { name: 'model', prompt: 'Model name(s) (comma-separated)', required: true, }, { name: 'timeout', prompt: 'Request timeout (ms)', default: '30000', validator: value => { if (!value) return undefined; const num = Number(value); if (Number.isNaN(num) || num <= 0) { return 'Timeout must be a positive number'; } return undefined; }, }, ], buildConfig: answers => { const config = { name: answers.providerName, baseUrl: answers.baseUrl, models: answers.model .split(',') .map(m => m.trim()) .filter(Boolean), }; if (answers.apiKey) { config.apiKey = answers.apiKey; } if (answers.timeout) { config.timeout = Number(answers.timeout); } return config; }, }, ]; //# sourceMappingURL=provider-templates.js.map