Co-authored-by: Max Tuzzolino <maxtuzz@Maxs-MacBook-Pro.local>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Max Tuzzolino <max.tuzsmith@gmail.com>
Co-authored-by: Ralph Khreish <35776126+Crunchyman-ralph@users.noreply.github.com>
This commit is contained in:
Eyal Toledano
2025-09-19 18:08:20 -04:00
committed by GitHub
parent 4e126430a0
commit fce841490a
55 changed files with 3559 additions and 693 deletions

View File

@@ -0,0 +1,155 @@
/**
* @fileoverview Error handling utilities for Grok CLI provider
*/
import { APICallError, LoadAPIKeyError } from '@ai-sdk/provider';
/**
* @typedef {import('./types.js').GrokCliErrorMetadata} GrokCliErrorMetadata
*/
/**
* Create an API call error with Grok CLI specific metadata
* @param {Object} params - Error parameters
* @param {string} params.message - Error message
* @param {string} [params.code] - Error code
* @param {number} [params.exitCode] - Process exit code
* @param {string} [params.stderr] - Standard error output
* @param {string} [params.stdout] - Standard output
* @param {string} [params.promptExcerpt] - Excerpt of the prompt
* @param {boolean} [params.isRetryable=false] - Whether the error is retryable
* @returns {APICallError}
*/
export function createAPICallError({
message,
code,
exitCode,
stderr,
stdout,
promptExcerpt,
isRetryable = false
}) {
/** @type {GrokCliErrorMetadata} */
const metadata = {
code,
exitCode,
stderr,
stdout,
promptExcerpt
};
return new APICallError({
message,
isRetryable,
url: 'grok-cli://command',
requestBodyValues: promptExcerpt ? { prompt: promptExcerpt } : undefined,
data: metadata
});
}
/**
* Create an authentication error
* @param {Object} params - Error parameters
* @param {string} params.message - Error message
* @returns {LoadAPIKeyError}
*/
export function createAuthenticationError({ message }) {
return new LoadAPIKeyError({
message:
message ||
'Authentication failed. Please ensure Grok CLI is properly configured with API key.'
});
}
/**
* Create a timeout error
* @param {Object} params - Error parameters
* @param {string} params.message - Error message
* @param {string} [params.promptExcerpt] - Excerpt of the prompt
* @param {number} params.timeoutMs - Timeout in milliseconds
* @returns {APICallError}
*/
export function createTimeoutError({ message, promptExcerpt, timeoutMs }) {
/** @type {GrokCliErrorMetadata & { timeoutMs: number }} */
const metadata = {
code: 'TIMEOUT',
promptExcerpt,
timeoutMs
};
return new APICallError({
message,
isRetryable: true,
url: 'grok-cli://command',
requestBodyValues: promptExcerpt ? { prompt: promptExcerpt } : undefined,
data: metadata
});
}
/**
* Create a CLI installation error
* @param {Object} params - Error parameters
* @param {string} [params.message] - Error message
* @returns {APICallError}
*/
export function createInstallationError({ message }) {
return new APICallError({
message:
message ||
'Grok CLI is not installed or not found in PATH. Please install with: npm install -g @vibe-kit/grok-cli',
isRetryable: false,
url: 'grok-cli://installation'
});
}
/**
* Check if an error is an authentication error
* @param {unknown} error - Error to check
* @returns {boolean}
*/
export function isAuthenticationError(error) {
if (error instanceof LoadAPIKeyError) return true;
if (
error instanceof APICallError &&
/** @type {GrokCliErrorMetadata} */ (error.data)?.exitCode === 401
)
return true;
return false;
}
/**
* Check if an error is a timeout error
* @param {unknown} error - Error to check
* @returns {boolean}
*/
export function isTimeoutError(error) {
if (
error instanceof APICallError &&
/** @type {GrokCliErrorMetadata} */ (error.data)?.code === 'TIMEOUT'
)
return true;
return false;
}
/**
* Check if an error is an installation error
* @param {unknown} error - Error to check
* @returns {boolean}
*/
export function isInstallationError(error) {
if (error instanceof APICallError && error.url === 'grok-cli://installation')
return true;
return false;
}
/**
* Get error metadata from an error
* @param {unknown} error - Error to extract metadata from
* @returns {GrokCliErrorMetadata|undefined}
*/
export function getErrorMetadata(error) {
if (error instanceof APICallError && error.data) {
return /** @type {GrokCliErrorMetadata} */ (error.data);
}
return undefined;
}

View File

@@ -0,0 +1,85 @@
/**
* @fileoverview Grok CLI provider factory and exports
*/
import { NoSuchModelError } from '@ai-sdk/provider';
import { GrokCliLanguageModel } from './language-model.js';
/**
* @typedef {import('./types.js').GrokCliSettings} GrokCliSettings
* @typedef {import('./types.js').GrokCliModelId} GrokCliModelId
* @typedef {import('./types.js').GrokCliProvider} GrokCliProvider
* @typedef {import('./types.js').GrokCliProviderSettings} GrokCliProviderSettings
*/
/**
* Create a Grok CLI provider
* @param {GrokCliProviderSettings} [options={}] - Provider configuration options
* @returns {GrokCliProvider} Grok CLI provider instance
*/
export function createGrokCli(options = {}) {
/**
* Create a language model instance
* @param {GrokCliModelId} modelId - Model ID
* @param {GrokCliSettings} [settings={}] - Model settings
* @returns {GrokCliLanguageModel}
*/
const createModel = (modelId, settings = {}) => {
return new GrokCliLanguageModel({
id: modelId,
settings: {
...options.defaultSettings,
...settings
}
});
};
/**
* Provider function
* @param {GrokCliModelId} modelId - Model ID
* @param {GrokCliSettings} [settings] - Model settings
* @returns {GrokCliLanguageModel}
*/
const provider = function (modelId, settings) {
if (new.target) {
throw new Error(
'The Grok CLI model function cannot be called with the new keyword.'
);
}
return createModel(modelId, settings);
};
provider.languageModel = createModel;
provider.chat = createModel; // Alias for languageModel
// Add textEmbeddingModel method that throws NoSuchModelError
provider.textEmbeddingModel = (modelId) => {
throw new NoSuchModelError({
modelId,
modelType: 'textEmbeddingModel'
});
};
return /** @type {GrokCliProvider} */ (provider);
}
/**
* Default Grok CLI provider instance
*/
export const grokCli = createGrokCli();
// Provider exports
export { GrokCliLanguageModel } from './language-model.js';
// Error handling exports
export {
isAuthenticationError,
isTimeoutError,
isInstallationError,
getErrorMetadata,
createAPICallError,
createAuthenticationError,
createTimeoutError,
createInstallationError
} from './errors.js';

View File

@@ -0,0 +1,59 @@
/**
* @fileoverview Extract JSON from Grok's response, handling markdown blocks and other formatting
*/
/**
* Extract JSON from Grok's response
* @param {string} text - The text to extract JSON from
* @returns {string} - The extracted JSON string
*/
export function extractJson(text) {
// Remove markdown code blocks if present
let jsonText = text.trim();
// Remove ```json blocks
jsonText = jsonText.replace(/^```json\s*/gm, '');
jsonText = jsonText.replace(/^```\s*/gm, '');
jsonText = jsonText.replace(/```\s*$/gm, '');
// Remove common TypeScript/JavaScript patterns
jsonText = jsonText.replace(/^const\s+\w+\s*=\s*/, ''); // Remove "const varName = "
jsonText = jsonText.replace(/^let\s+\w+\s*=\s*/, ''); // Remove "let varName = "
jsonText = jsonText.replace(/^var\s+\w+\s*=\s*/, ''); // Remove "var varName = "
jsonText = jsonText.replace(/;?\s*$/, ''); // Remove trailing semicolons
// Try to extract JSON object or array
const objectMatch = jsonText.match(/{[\s\S]*}/);
const arrayMatch = jsonText.match(/\[[\s\S]*\]/);
if (objectMatch) {
jsonText = objectMatch[0];
} else if (arrayMatch) {
jsonText = arrayMatch[0];
}
// First try to parse as valid JSON
try {
JSON.parse(jsonText);
return jsonText;
} catch {
// If it's not valid JSON, it might be a JavaScript object literal
// Try to convert it to valid JSON
try {
// This is a simple conversion that handles basic cases
// Replace unquoted keys with quoted keys
const converted = jsonText
.replace(/([{,]\s*)([a-zA-Z_$][a-zA-Z0-9_$]*)\s*:/g, '$1"$2":')
// Replace single quotes with double quotes
.replace(/'/g, '"');
// Validate the converted JSON
JSON.parse(converted);
return converted;
} catch {
// If all else fails, return the original text
// The AI SDK will handle the error appropriately
return text;
}
}
}

View File

@@ -0,0 +1,407 @@
/**
* @fileoverview Grok CLI Language Model implementation
*/
import { NoSuchModelError } from '@ai-sdk/provider';
import { generateId } from '@ai-sdk/provider-utils';
import {
createPromptFromMessages,
convertFromGrokCliResponse,
escapeShellArg
} from './message-converter.js';
import { extractJson } from './json-extractor.js';
import {
createAPICallError,
createAuthenticationError,
createInstallationError,
createTimeoutError
} from './errors.js';
import { spawn } from 'child_process';
import { promises as fs } from 'fs';
import { join } from 'path';
import { homedir } from 'os';
/**
* @typedef {import('./types.js').GrokCliSettings} GrokCliSettings
* @typedef {import('./types.js').GrokCliModelId} GrokCliModelId
*/
/**
* @typedef {Object} GrokCliLanguageModelOptions
* @property {GrokCliModelId} id - Model ID
* @property {GrokCliSettings} [settings] - Model settings
*/
export class GrokCliLanguageModel {
specificationVersion = 'v1';
defaultObjectGenerationMode = 'json';
supportsImageUrls = false;
supportsStructuredOutputs = false;
/** @type {GrokCliModelId} */
modelId;
/** @type {GrokCliSettings} */
settings;
/**
* @param {GrokCliLanguageModelOptions} options
*/
constructor(options) {
this.modelId = options.id;
this.settings = options.settings ?? {};
// Validate model ID format
if (
!this.modelId ||
typeof this.modelId !== 'string' ||
this.modelId.trim() === ''
) {
throw new NoSuchModelError({
modelId: this.modelId,
modelType: 'languageModel'
});
}
}
get provider() {
return 'grok-cli';
}
/**
* Check if Grok CLI is installed and available
* @returns {Promise<boolean>}
*/
async checkGrokCliInstallation() {
return new Promise((resolve) => {
const child = spawn('grok', ['--version'], {
stdio: 'pipe'
});
child.on('error', () => resolve(false));
child.on('exit', (code) => resolve(code === 0));
});
}
/**
* Get API key from settings or environment
* @returns {Promise<string|null>}
*/
async getApiKey() {
// Check settings first
if (this.settings.apiKey) {
return this.settings.apiKey;
}
// Check environment variable
if (process.env.GROK_CLI_API_KEY) {
return process.env.GROK_CLI_API_KEY;
}
// Check grok-cli config file
try {
const configPath = join(homedir(), '.grok', 'user-settings.json');
const configContent = await fs.readFile(configPath, 'utf8');
const config = JSON.parse(configContent);
return config.apiKey || null;
} catch (error) {
return null;
}
}
/**
* Execute Grok CLI command
* @param {Array<string>} args - Command line arguments
* @param {Object} options - Execution options
* @returns {Promise<{stdout: string, stderr: string, exitCode: number}>}
*/
async executeGrokCli(args, options = {}) {
const timeout = options.timeout || this.settings.timeout || 120000; // 2 minutes default
return new Promise((resolve, reject) => {
const child = spawn('grok', args, {
stdio: 'pipe',
cwd: this.settings.workingDirectory || process.cwd()
});
let stdout = '';
let stderr = '';
let timeoutId;
// Set up timeout
if (timeout > 0) {
timeoutId = setTimeout(() => {
child.kill('SIGTERM');
reject(
createTimeoutError({
message: `Grok CLI command timed out after ${timeout}ms`,
timeoutMs: timeout,
promptExcerpt: args.join(' ').substring(0, 200)
})
);
}, timeout);
}
child.stdout.on('data', (data) => {
stdout += data.toString();
});
child.stderr.on('data', (data) => {
stderr += data.toString();
});
child.on('error', (error) => {
if (timeoutId) clearTimeout(timeoutId);
if (error.code === 'ENOENT') {
reject(createInstallationError({}));
} else {
reject(
createAPICallError({
message: `Failed to execute Grok CLI: ${error.message}`,
code: error.code,
stderr: error.message,
isRetryable: false
})
);
}
});
child.on('exit', (exitCode) => {
if (timeoutId) clearTimeout(timeoutId);
resolve({
stdout: stdout.trim(),
stderr: stderr.trim(),
exitCode: exitCode || 0
});
});
});
}
/**
* Generate unsupported parameter warnings
* @param {Object} options - Generation options
* @returns {Array} Warnings array
*/
generateUnsupportedWarnings(options) {
const warnings = [];
const unsupportedParams = [];
// Grok CLI supports some parameters but not all AI SDK parameters
if (options.topP !== undefined) unsupportedParams.push('topP');
if (options.topK !== undefined) unsupportedParams.push('topK');
if (options.presencePenalty !== undefined)
unsupportedParams.push('presencePenalty');
if (options.frequencyPenalty !== undefined)
unsupportedParams.push('frequencyPenalty');
if (options.stopSequences !== undefined && options.stopSequences.length > 0)
unsupportedParams.push('stopSequences');
if (options.seed !== undefined) unsupportedParams.push('seed');
if (unsupportedParams.length > 0) {
for (const param of unsupportedParams) {
warnings.push({
type: 'unsupported-setting',
setting: param,
details: `Grok CLI does not support the ${param} parameter. It will be ignored.`
});
}
}
return warnings;
}
/**
* Generate text using Grok CLI
* @param {Object} options - Generation options
* @returns {Promise<Object>}
*/
async doGenerate(options) {
// Check CLI installation
const isInstalled = await this.checkGrokCliInstallation();
if (!isInstalled) {
throw createInstallationError({});
}
// Get API key
const apiKey = await this.getApiKey();
if (!apiKey) {
throw createAuthenticationError({
message:
'Grok CLI API key not found. Set GROK_CLI_API_KEY environment variable or configure grok-cli.'
});
}
const prompt = createPromptFromMessages(options.prompt);
const warnings = this.generateUnsupportedWarnings(options);
// Build command arguments
const args = ['--prompt', escapeShellArg(prompt)];
// Add model if specified
if (this.modelId && this.modelId !== 'default') {
args.push('--model', this.modelId);
}
// Add API key if available
if (apiKey) {
args.push('--api-key', apiKey);
}
// Add base URL if provided in settings
if (this.settings.baseURL) {
args.push('--base-url', this.settings.baseURL);
}
// Add working directory if specified
if (this.settings.workingDirectory) {
args.push('--directory', this.settings.workingDirectory);
}
try {
const result = await this.executeGrokCli(args, {
timeout: this.settings.timeout
});
if (result.exitCode !== 0) {
// Handle authentication errors
if (
result.stderr.toLowerCase().includes('unauthorized') ||
result.stderr.toLowerCase().includes('authentication')
) {
throw createAuthenticationError({
message: `Grok CLI authentication failed: ${result.stderr}`
});
}
throw createAPICallError({
message: `Grok CLI failed with exit code ${result.exitCode}: ${result.stderr || 'Unknown error'}`,
exitCode: result.exitCode,
stderr: result.stderr,
stdout: result.stdout,
promptExcerpt: prompt.substring(0, 200),
isRetryable: false
});
}
// Parse response
const response = convertFromGrokCliResponse(result.stdout);
let text = response.text || '';
// Extract JSON if in object-json mode
if (options.mode?.type === 'object-json' && text) {
text = extractJson(text);
}
return {
text: text || undefined,
usage: response.usage || { promptTokens: 0, completionTokens: 0 },
finishReason: 'stop',
rawCall: {
rawPrompt: prompt,
rawSettings: args
},
warnings: warnings.length > 0 ? warnings : undefined,
response: {
id: generateId(),
timestamp: new Date(),
modelId: this.modelId
},
request: {
body: prompt
},
providerMetadata: {
'grok-cli': {
exitCode: result.exitCode,
stderr: result.stderr || undefined
}
}
};
} catch (error) {
// Re-throw our custom errors
if (error.name === 'APICallError' || error.name === 'LoadAPIKeyError') {
throw error;
}
// Wrap other errors
throw createAPICallError({
message: `Grok CLI execution failed: ${error.message}`,
code: error.code,
promptExcerpt: prompt.substring(0, 200),
isRetryable: false
});
}
}
/**
* Stream text using Grok CLI
* Note: Grok CLI doesn't natively support streaming, so this simulates streaming
* by generating the full response and then streaming it in chunks
* @param {Object} options - Stream options
* @returns {Promise<Object>}
*/
async doStream(options) {
const warnings = this.generateUnsupportedWarnings(options);
const stream = new ReadableStream({
start: async (controller) => {
try {
// Generate the full response first
const result = await this.doGenerate(options);
// Emit response metadata
controller.enqueue({
type: 'response-metadata',
id: result.response.id,
timestamp: result.response.timestamp,
modelId: result.response.modelId
});
// Simulate streaming by chunking the text
const text = result.text || '';
const chunkSize = 50; // Characters per chunk
for (let i = 0; i < text.length; i += chunkSize) {
const chunk = text.slice(i, i + chunkSize);
controller.enqueue({
type: 'text-delta',
textDelta: chunk
});
// Add small delay to simulate streaming
await new Promise((resolve) => setTimeout(resolve, 20));
}
// Emit finish event
controller.enqueue({
type: 'finish',
finishReason: result.finishReason,
usage: result.usage,
providerMetadata: result.providerMetadata
});
controller.close();
} catch (error) {
controller.enqueue({
type: 'error',
error
});
controller.close();
}
}
});
return {
stream,
rawCall: {
rawPrompt: createPromptFromMessages(options.prompt),
rawSettings: {}
},
warnings: warnings.length > 0 ? warnings : undefined,
request: {
body: createPromptFromMessages(options.prompt)
}
};
}
}

View File

@@ -0,0 +1,135 @@
/**
* @fileoverview Message format conversion utilities for Grok CLI provider
*/
/**
* @typedef {import('./types.js').GrokCliMessage} GrokCliMessage
*/
/**
* Convert AI SDK messages to Grok CLI compatible format
* @param {Array<Object>} messages - AI SDK message array
* @returns {Array<GrokCliMessage>} Grok CLI compatible messages
*/
export function convertToGrokCliMessages(messages) {
return messages.map((message) => {
// Handle different message content types
let content = '';
if (typeof message.content === 'string') {
content = message.content;
} else if (Array.isArray(message.content)) {
// Handle multi-part content (text and images)
content = message.content
.filter((part) => part.type === 'text')
.map((part) => part.text)
.join('\n');
} else if (message.content && typeof message.content === 'object') {
// Handle object content
content = message.content.text || JSON.stringify(message.content);
}
return {
role: message.role,
content: content.trim()
};
});
}
/**
* Convert Grok CLI response to AI SDK format
* @param {string} responseText - Raw response text from Grok CLI (JSONL format)
* @returns {Object} AI SDK compatible response object
*/
export function convertFromGrokCliResponse(responseText) {
try {
// Grok CLI outputs JSONL format - each line is a separate JSON message
const lines = responseText
.trim()
.split('\n')
.filter((line) => line.trim());
// Parse each line as JSON and find assistant messages
const messages = [];
for (const line of lines) {
try {
const message = JSON.parse(line);
messages.push(message);
} catch (parseError) {
// Skip invalid JSON lines
continue;
}
}
// Find the last assistant message
const assistantMessage = messages
.filter((msg) => msg.role === 'assistant')
.pop();
if (assistantMessage && assistantMessage.content) {
return {
text: assistantMessage.content,
usage: assistantMessage.usage
? {
promptTokens: assistantMessage.usage.prompt_tokens || 0,
completionTokens: assistantMessage.usage.completion_tokens || 0,
totalTokens: assistantMessage.usage.total_tokens || 0
}
: undefined
};
}
// Fallback: if no assistant message found, return the raw text
return {
text: responseText.trim(),
usage: undefined
};
} catch (error) {
// If parsing fails completely, treat as plain text response
return {
text: responseText.trim(),
usage: undefined
};
}
}
/**
* Create a prompt string for Grok CLI from messages
* @param {Array<Object>} messages - AI SDK message array
* @returns {string} Formatted prompt string
*/
export function createPromptFromMessages(messages) {
const grokMessages = convertToGrokCliMessages(messages);
// Create a conversation-style prompt
const prompt = grokMessages
.map((message) => {
switch (message.role) {
case 'system':
return `System: ${message.content}`;
case 'user':
return `User: ${message.content}`;
case 'assistant':
return `Assistant: ${message.content}`;
default:
return `${message.role}: ${message.content}`;
}
})
.join('\n\n');
return prompt;
}
/**
* Escape shell arguments for safe CLI execution
* @param {string} arg - Argument to escape
* @returns {string} Shell-escaped argument
*/
export function escapeShellArg(arg) {
if (typeof arg !== 'string') {
arg = String(arg);
}
// Replace single quotes with '\''
return "'" + arg.replace(/'/g, "'\\''") + "'";
}

View File

@@ -0,0 +1,56 @@
/**
* @fileoverview Type definitions for Grok CLI provider
*/
/**
* @typedef {Object} GrokCliSettings
* @property {string} [apiKey] - API key for Grok CLI
* @property {string} [baseURL] - Base URL for Grok API
* @property {string} [model] - Default model to use
* @property {number} [timeout] - Timeout in milliseconds
* @property {string} [workingDirectory] - Working directory for CLI commands
*/
/**
* @typedef {string} GrokCliModelId
* Model identifiers supported by Grok CLI
*/
/**
* @typedef {Object} GrokCliErrorMetadata
* @property {string} [code] - Error code
* @property {number} [exitCode] - Process exit code
* @property {string} [stderr] - Standard error output
* @property {string} [stdout] - Standard output
* @property {string} [promptExcerpt] - Excerpt of the prompt that caused the error
* @property {number} [timeoutMs] - Timeout value in milliseconds
*/
/**
* @typedef {Function} GrokCliProvider
* @property {Function} languageModel - Create a language model
* @property {Function} chat - Alias for languageModel
* @property {Function} textEmbeddingModel - Text embedding model (throws error)
*/
/**
* @typedef {Object} GrokCliProviderSettings
* @property {GrokCliSettings} [defaultSettings] - Default settings for all models
*/
/**
* @typedef {Object} GrokCliMessage
* @property {string} role - Message role (user, assistant, system)
* @property {string} content - Message content
*/
/**
* @typedef {Object} GrokCliResponse
* @property {string} content - Response content
* @property {Object} [usage] - Token usage information
* @property {number} [usage.prompt_tokens] - Input tokens used
* @property {number} [usage.completion_tokens] - Output tokens used
* @property {number} [usage.total_tokens] - Total tokens used
*/
export {};

View File

@@ -0,0 +1,79 @@
/**
* grok-cli.js
* AI provider implementation for Grok models using Grok CLI.
*/
import { createGrokCli } from './custom-sdk/grok-cli/index.js';
import { BaseAIProvider } from './base-provider.js';
import { getGrokCliSettingsForCommand } from '../../scripts/modules/config-manager.js';
export class GrokCliProvider extends BaseAIProvider {
constructor() {
super();
this.name = 'Grok CLI';
}
/**
* Returns the environment variable name required for this provider's API key.
* @returns {string} The environment variable name for the Grok API key
*/
getRequiredApiKeyName() {
return 'GROK_CLI_API_KEY';
}
/**
* Override to indicate that API key is optional since Grok CLI can be configured separately
* @returns {boolean} False since Grok CLI can use its own config
*/
isRequiredApiKey() {
return false; // Grok CLI can use its own config file
}
/**
* Override validateAuth to be more flexible with API key validation
* @param {object} params - Parameters to validate
*/
validateAuth(params) {
// Grok CLI can work with:
// 1. API key passed in params
// 2. Environment variable GROK_CLI_API_KEY
// 3. Grok CLI's own config file (~/.grok/user-settings.json)
// So we don't enforce API key requirement here
// Suppress unused parameter warning
void params;
}
/**
* Creates and returns a Grok CLI client instance.
* @param {object} params - Parameters for client initialization
* @param {string} [params.apiKey] - Grok CLI API key (optional if configured in CLI)
* @param {string} [params.baseURL] - Optional custom API endpoint
* @param {string} [params.workingDirectory] - Working directory for CLI commands
* @param {number} [params.timeout] - Timeout for CLI commands in milliseconds
* @param {string} [params.commandName] - Name of the command invoking the service
* @returns {Function} Grok CLI client function
* @throws {Error} If initialization fails
*/
getClient(params) {
try {
const { apiKey, baseURL, workingDirectory, timeout, commandName } =
params;
// Get Grok CLI settings from config
const grokCliSettings = getGrokCliSettingsForCommand(commandName);
return createGrokCli({
defaultSettings: {
apiKey,
baseURL,
workingDirectory:
workingDirectory || grokCliSettings.workingDirectory,
timeout: timeout || grokCliSettings.timeout,
defaultModel: grokCliSettings.defaultModel
}
});
} catch (error) {
this.handleError('client initialization', error);
}
}
}

View File

@@ -16,3 +16,4 @@ export { AzureProvider } from './azure.js';
export { VertexAIProvider } from './google-vertex.js';
export { ClaudeCodeProvider } from './claude-code.js';
export { GeminiCliProvider } from './gemini-cli.js';
export { GrokCliProvider } from './grok-cli.js';