@webdevtoday/grok-cli
Version:
A sophisticated CLI tool for interacting with xAI Grok 4, featuring conversation history, file reference, custom commands, memory system, and genetic development workflows
346 lines • 12.2 kB
JavaScript
;
/**
* Configuration management system for Grok CLI
* Supports hierarchical configuration loading and Zod validation
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConfigManager = void 0;
const zod_1 = require("zod");
const fs_extra_1 = require("fs-extra");
const path_1 = require("path");
const os_1 = require("os");
const yaml_1 = __importDefault(require("yaml"));
const types_1 = require("../types");
// Zod schemas for configuration validation
const HookCommandSchema = zod_1.z.object({
type: zod_1.z.literal('command'),
command: zod_1.z.string(),
timeout: zod_1.z.number().positive().optional(),
});
const HookMatcherSchema = zod_1.z.object({
matcher: zod_1.z.string().optional(),
hooks: zod_1.z.array(HookCommandSchema),
});
const HookConfigurationSchema = zod_1.z.record(zod_1.z.array(HookMatcherSchema));
const PermissionConfigurationSchema = zod_1.z.object({
mode: zod_1.z.enum(['ask', 'plan', 'full']),
allowedTools: zod_1.z.array(zod_1.z.string()),
disallowedTools: zod_1.z.array(zod_1.z.string()),
});
const McpServerConfigSchema = zod_1.z.object({
command: zod_1.z.string(),
args: zod_1.z.array(zod_1.z.string()),
env: zod_1.z.record(zod_1.z.string()).optional(),
cwd: zod_1.z.string().optional(),
});
const McpConfigurationSchema = zod_1.z.object({
servers: zod_1.z.record(McpServerConfigSchema),
});
const MemoryConfigurationSchema = zod_1.z.object({
autoLoad: zod_1.z.boolean(),
maxDepth: zod_1.z.number().positive(),
excludePatterns: zod_1.z.array(zod_1.z.string()),
});
const GrokConfigSchema = zod_1.z.object({
model: zod_1.z.string(),
apiKeys: zod_1.z.object({
xai: zod_1.z.string().optional(),
openai: zod_1.z.string().optional(),
composio: zod_1.z.string().optional(),
}).transform((keys) => ({
xai: keys.xai || undefined,
openai: keys.openai || undefined,
composio: keys.composio || undefined,
})),
hooks: HookConfigurationSchema,
permissions: PermissionConfigurationSchema,
mcp: McpConfigurationSchema,
memory: MemoryConfigurationSchema,
});
/**
* Configuration manager handles loading, validation, and merging of config files
*/
class ConfigManager {
constructor() {
this.config = null;
this.configPaths = [];
}
static getInstance() {
if (!ConfigManager.instance) {
ConfigManager.instance = new ConfigManager();
}
return ConfigManager.instance;
}
/**
* Load configuration from all available sources
*/
async loadConfig(cwd = process.cwd()) {
const configs = [];
// Define configuration file paths in priority order
this.configPaths = [
(0, path_1.join)((0, os_1.homedir)(), '.grok', 'settings.json'),
(0, path_1.join)((0, os_1.homedir)(), '.grok', 'settings.yaml'),
(0, path_1.join)(cwd, '.grok', 'settings.json'),
(0, path_1.join)(cwd, '.grok', 'settings.yaml'),
(0, path_1.join)(cwd, '.grok', 'settings.local.json'),
(0, path_1.join)(cwd, '.grok', 'settings.local.yaml'),
];
// Load each config file if it exists
for (const configPath of this.configPaths) {
if (await (0, fs_extra_1.pathExists)(configPath)) {
try {
const configData = await this.loadConfigFile(configPath);
configs.push(configData);
}
catch (error) {
console.warn(`Failed to load config from ${configPath}:`, error);
}
}
}
// Load API keys from files
const apiKeyConfig = await this.loadApiKeysFromFiles(cwd);
// Merge configurations with environment variables
const mergedConfig = this.mergeConfigs([
this.getDefaultConfig(),
...configs,
apiKeyConfig,
this.getEnvironmentConfig(),
]);
// Validate final configuration
try {
this.config = GrokConfigSchema.parse(mergedConfig);
return this.config;
}
catch (error) {
throw new types_1.ConfigurationError('Invalid configuration', { zodError: error, mergedConfig });
}
}
/**
* Get current configuration
*/
getConfig() {
if (!this.config) {
throw new types_1.ConfigurationError('Configuration not loaded. Call loadConfig() first.');
}
return this.config;
}
/**
* Update configuration and save to local settings
*/
async updateConfig(updates, cwd = process.cwd()) {
if (!this.config) {
throw new types_1.ConfigurationError('Configuration not loaded. Call loadConfig() first.');
}
const updatedConfig = this.mergeConfigs([this.config, updates]);
try {
this.config = GrokConfigSchema.parse(updatedConfig);
}
catch (error) {
throw new types_1.ConfigurationError('Invalid configuration update', { zodError: error, updates });
}
// Save to local settings file
const localConfigPath = (0, path_1.join)(cwd, '.grok', 'settings.local.json');
await this.saveConfigFile(localConfigPath, updates);
}
/**
* Load a single configuration file
*/
async loadConfigFile(filePath) {
const content = await (0, fs_extra_1.readFile)(filePath, 'utf-8');
if (filePath.endsWith('.yaml') || filePath.endsWith('.yml')) {
return yaml_1.default.parse(content);
}
else {
return JSON.parse(content);
}
}
/**
* Save configuration to file
*/
async saveConfigFile(filePath, config) {
const { ensureDir, writeFile } = await Promise.resolve().then(() => __importStar(require('fs-extra')));
await ensureDir((0, path_1.resolve)(filePath, '..'));
if (filePath.endsWith('.yaml') || filePath.endsWith('.yml')) {
await writeFile(filePath, yaml_1.default.stringify(config), 'utf-8');
}
else {
await writeFile(filePath, JSON.stringify(config, null, 2), 'utf-8');
}
}
/**
* Get default configuration
*/
getDefaultConfig() {
return {
model: 'grok-4-0709',
apiKeys: {
xai: undefined,
openai: undefined,
composio: undefined,
},
hooks: {},
permissions: {
mode: 'ask',
allowedTools: [],
disallowedTools: [],
},
mcp: {
servers: {},
},
memory: {
autoLoad: true,
maxDepth: 5,
excludePatterns: ['node_modules/**', '.git/**', 'dist/**'],
},
};
}
/**
* Load API keys from files (local takes precedence over global)
*/
async loadApiKeysFromFiles(cwd) {
const apiKeyConfig = {
apiKeys: {
xai: undefined,
openai: undefined,
composio: undefined,
},
};
// Check for local API key file (.grok-api in current directory)
const localApiKeyFile = (0, path_1.join)(cwd, '.grok-api');
if (await (0, fs_extra_1.pathExists)(localApiKeyFile)) {
try {
const content = await (0, fs_extra_1.readFile)(localApiKeyFile, 'utf-8');
const apiKey = content.trim();
if (apiKey) {
apiKeyConfig.apiKeys.xai = apiKey;
}
}
catch (error) {
console.warn(`Failed to load local API key from ${localApiKeyFile}:`, error);
}
}
else {
// Check for global API key file (~/.grok/api-key)
const globalApiKeyFile = (0, path_1.join)((0, os_1.homedir)(), '.grok', 'api-key');
if (await (0, fs_extra_1.pathExists)(globalApiKeyFile)) {
try {
const content = await (0, fs_extra_1.readFile)(globalApiKeyFile, 'utf-8');
const apiKey = content.trim();
if (apiKey) {
apiKeyConfig.apiKeys.xai = apiKey;
}
}
catch (error) {
console.warn(`Failed to load global API key from ${globalApiKeyFile}:`, error);
}
}
}
return apiKeyConfig;
}
/**
* Get configuration from environment variables
*/
getEnvironmentConfig() {
const envConfig = {
apiKeys: {
xai: undefined,
openai: undefined,
composio: undefined,
},
};
if (process.env['XAI_API_KEY']) {
envConfig.apiKeys.xai = process.env['XAI_API_KEY'];
}
if (process.env['OPENAI_API_KEY']) {
envConfig.apiKeys.openai = process.env['OPENAI_API_KEY'];
}
if (process.env['COMPOSIO_API_KEY']) {
envConfig.apiKeys.composio = process.env['COMPOSIO_API_KEY'];
}
if (process.env['GROK_MODEL']) {
envConfig.model = process.env['GROK_MODEL'];
}
return envConfig;
}
/**
* Deep merge multiple configuration objects
*/
mergeConfigs(configs) {
const result = {};
for (const config of configs) {
this.deepMerge(result, config);
}
return result;
}
/**
* Deep merge two objects
*/
deepMerge(target, source) {
for (const key in source) {
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
if (!target[key])
target[key] = {};
this.deepMerge(target[key], source[key]);
}
else {
target[key] = source[key];
}
}
return target;
}
/**
* Validate configuration against schema
*/
validateConfig(config) {
try {
return GrokConfigSchema.parse(config);
}
catch (error) {
throw new types_1.ConfigurationError('Configuration validation failed', { zodError: error, config });
}
}
/**
* Get configuration file paths that were checked
*/
getConfigPaths() {
return [...this.configPaths];
}
}
exports.ConfigManager = ConfigManager;
//# sourceMappingURL=config.js.map