capsule-ai-cli
Version:
The AI Model Orchestrator - Intelligent multi-model workflows with device-locked licensing
179 lines • 5.63 kB
JavaScript
import Conf from 'conf';
import { cosmiconfig } from 'cosmiconfig';
import chalk from 'chalk';
import { z } from 'zod';
const ConfigSchema = z.object({
auth: z.object({
token: z.string().optional(),
email: z.string().optional(),
tier: z.enum(['base', 'super']).optional(),
deviceId: z.string().optional(),
remainingRUs: z.number().optional(),
validUntil: z.string().optional()
}).optional(),
providers: z.record(z.string(), z.object({
apiKey: z.string().optional(),
baseUrl: z.string().optional(),
defaultModel: z.string().optional(),
enabled: z.boolean().default(true),
useManaged: z.boolean().default(false),
isLocal: z.boolean().optional()
})).default({}),
defaultProvider: z.string().optional(),
ui: z.object({
theme: z.enum(['dark', 'light']).default('dark'),
showCosts: z.boolean().default(true),
animations: z.boolean().default(true)
}).default({
theme: 'dark',
showCosts: true,
animations: true
}),
tools: z.object({
fileOperations: z.boolean().default(true),
shellExecution: z.boolean().default(true),
webAccess: z.boolean().default(true)
}).default({
fileOperations: true,
shellExecution: true,
webAccess: true
}),
costs: z.object({
trackUsage: z.boolean().default(true),
budgetAlerts: z.boolean().default(true),
monthlyLimit: z.number().optional()
}).default({
trackUsage: true,
budgetAlerts: true
}),
services: z.object({
googleSearch: z.object({
apiKey: z.string().optional(),
searchEngineId: z.string().optional()
}).optional(),
serpApi: z.object({
apiKey: z.string().optional()
}).optional()
}).optional()
});
export class ConfigManager {
store;
fileConfig = null;
constructor() {
this.store = new Conf({
projectName: 'capsule-ai-cli'
});
}
async loadConfig() {
const explorer = cosmiconfig('capsule', {
searchPlaces: [
'.capsulerc',
'.capsulerc.json',
'.capsulerc.yaml',
'.capsulerc.yml',
'.capsule/config.yaml',
'.capsule/config.json',
'capsule.config.js'
]
});
try {
const result = await explorer.search();
if (result) {
const parsed = ConfigSchema.parse(result.config);
this.fileConfig = {
...parsed,
providers: parsed.providers || {}
};
}
}
catch (error) {
console.warn(chalk.yellow('Warning: Invalid config file'), error);
}
return this.getConfig();
}
getConfig() {
const stored = this.store.store;
const defaultConfig = {
providers: {},
ui: {
theme: 'dark',
showCosts: true,
animations: true
},
tools: {
fileOperations: true,
shellExecution: true,
webAccess: true
},
costs: {
trackUsage: true,
budgetAlerts: true
}
};
const mergedConfig = this.mergeConfigs(defaultConfig, stored);
if (this.fileConfig) {
return this.mergeConfigs(mergedConfig, this.fileConfig);
}
return mergedConfig;
}
setConfig(path, value) {
this.store.set(path, value);
}
getApiKey(provider) {
const config = this.getConfig();
const providerConfig = config.providers[provider];
if (providerConfig?.apiKey) {
return providerConfig.apiKey;
}
const envVarMap = {
openai: 'OPENAI_API_KEY',
anthropic: 'ANTHROPIC_API_KEY',
google: 'GOOGLE_API_KEY',
gemini: 'GEMINI_API_KEY'
};
const envVar = envVarMap[provider];
return envVar ? process.env[envVar] : undefined;
}
validateProvider(provider) {
const apiKey = this.getApiKey(provider);
if (!apiKey) {
return {
valid: false,
error: `No API key found for ${provider}. Set it with: capsule config set providers.${provider}.apiKey YOUR_KEY`
};
}
const config = this.getConfig();
const providerConfig = config.providers[provider];
if (providerConfig && !providerConfig.enabled) {
return {
valid: false,
error: `Provider ${provider} is disabled`
};
}
return { valid: true };
}
mergeConfigs(base, override) {
return {
...base,
...override,
auth: override.auth || base.auth,
providers: { ...base.providers, ...override.providers },
ui: { ...base.ui, ...override.ui },
tools: { ...base.tools, ...override.tools },
costs: { ...base.costs, ...override.costs }
};
}
isAuthenticated() {
const config = this.getConfig();
return !!config.auth?.token;
}
getUserTier() {
const config = this.getConfig();
return config.auth?.tier;
}
}
export const configManager = new ConfigManager();
export async function loadConfig() {
return configManager.loadConfig();
}
//# sourceMappingURL=config.js.map