/**
* OpenClaw API Client
* Handles all HTTP communication with the OpenClaw API
*/
import axios, { AxiosInstance, AxiosError } from 'axios';
import type {
OpenClawConfig,
SendMessageParams,
SendMessageResponse,
ExecuteCommandParams,
ExecuteCommandResponse,
CreateCalendarEventParams,
CreateCalendarEventResponse,
SendEmailParams,
SendEmailResponse,
GetTaskStatusParams,
GetTaskStatusResponse,
RequestOptions,
} from './types.js';
/**
* Custom error class for OpenClaw API errors
*/
export class OpenClawApiError extends Error {
constructor(
public code: string,
message: string,
public details?: unknown
) {
super(message);
this.name = 'OpenClawApiError';
}
}
/**
* OpenClaw API Client
*/
export class OpenClawClient {
private client: AxiosInstance;
private config: OpenClawConfig;
constructor(config: OpenClawConfig) {
this.config = {
...config,
timeout: config.timeout || 30000,
maxRetries: config.maxRetries || 3,
};
this.client = axios.create({
baseURL: this.config.apiUrl,
timeout: this.config.timeout,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.config.apiKey}`,
'User-Agent': 'mcp-openclaw/1.0.0',
},
});
// Add response interceptor for error handling
this.client.interceptors.response.use(
(response) => response,
(error) => this.handleError(error)
);
}
/**
* Handle API errors with appropriate retries
*/
private async handleError(error: AxiosError): Promise<never> {
if (error.response) {
const status = error.response.status;
const data = error.response.data as { code?: string; message?: string; details?: unknown };
// Retry on 429 (rate limit) or 5xx errors
if ((status === 429 || status >= 500) && this.config.maxRetries) {
// Implementation could add retry logic here
throw new OpenClawApiError(
data.code || 'API_ERROR',
data.message || error.message,
data.details
);
}
throw new OpenClawApiError(
data.code || 'HTTP_ERROR',
data.message || `HTTP ${status}: ${error.message}`,
data.details
);
}
if (error.request) {
throw new OpenClawApiError(
'NETWORK_ERROR',
'No response received from server',
{ originalError: error.message }
);
}
throw new OpenClawApiError(
'REQUEST_ERROR',
error.message || 'Unknown error occurred'
);
}
/**
* Make a generic API request
*/
private async request<T>(endpoint: string, options: RequestOptions = {}): Promise<T> {
const { method = 'GET', headers, body, params } = options;
const response = await this.client.request<T>({
url: endpoint,
method,
headers,
data: body,
params,
});
return response.data;
}
/**
* Send a message to a platform
*/
async sendMessage(params: SendMessageParams): Promise<SendMessageResponse> {
try {
const response = await this.request<SendMessageResponse>('/v1/messages/send', {
method: 'POST',
body: params,
});
return response;
} catch (error) {
if (error instanceof OpenClawApiError) {
return {
success: false,
error: error.message,
};
}
return {
success: false,
error: 'Unknown error occurred',
};
}
}
/**
* Execute a command in the OpenClaw environment
*/
async executeCommand(params: ExecuteCommandParams): Promise<ExecuteCommandResponse> {
try {
const response = await this.request<ExecuteCommandResponse>('/v1/commands/execute', {
method: 'POST',
body: params,
});
return response;
} catch (error) {
if (error instanceof OpenClawApiError) {
return {
success: false,
error: error.message,
};
}
return {
success: false,
error: 'Unknown error occurred',
};
}
}
/**
* Create a calendar event
*/
async createCalendarEvent(params: CreateCalendarEventParams): Promise<CreateCalendarEventResponse> {
try {
const response = await this.request<CreateCalendarEventResponse>('/v1/calendar/events', {
method: 'POST',
body: params,
});
return response;
} catch (error) {
if (error instanceof OpenClawApiError) {
return {
success: false,
error: error.message,
};
}
return {
success: false,
error: 'Unknown error occurred',
};
}
}
/**
* Send an email
*/
async sendEmail(params: SendEmailParams): Promise<SendEmailResponse> {
try {
const response = await this.request<SendEmailResponse>('/v1/emails/send', {
method: 'POST',
body: params,
});
return response;
} catch (error) {
if (error instanceof OpenClawApiError) {
return {
success: false,
error: error.message,
};
}
return {
success: false,
error: 'Unknown error occurred',
};
}
}
/**
* Get the status of a task
*/
async getTaskStatus(params: GetTaskStatusParams): Promise<GetTaskStatusResponse> {
try {
const response = await this.request<GetTaskStatusResponse>(
`/v1/tasks/${params.taskId}/status`,
{
method: 'GET',
}
);
return response;
} catch (error) {
if (error instanceof OpenClawApiError) {
return {
success: false,
error: error.message,
};
}
return {
success: false,
error: 'Unknown error occurred',
};
}
}
/**
* Health check for the API
*/
async healthCheck(): Promise<{ healthy: boolean; version?: string }> {
try {
const response = await this.request<{ healthy: boolean; version?: string }>('/health', {
method: 'GET',
});
return response;
} catch {
return { healthy: false };
}
}
/**
* Close the client connection
*/
close(): void {
// Cleanup if needed
}
}
/**
* Create a new OpenClaw client from environment variables
*/
export function createClientFromEnv(): OpenClawClient {
const apiUrl = process.env.OPENCLAW_API_URL;
const apiKey = process.env.OPENCLAW_API_KEY;
if (!apiUrl) {
throw new Error('OPENCLAW_API_URL environment variable is required');
}
if (!apiKey) {
throw new Error('OPENCLAW_API_KEY environment variable is required');
}
return new OpenClawClient({
apiUrl,
apiKey,
timeout: parseInt(process.env.OPENCLAW_TIMEOUT || '30000', 10),
maxRetries: parseInt(process.env.OPENCLAW_MAX_RETRIES || '3', 10),
});
}