@codervisor/devlog-cli
Version:
Command-line interface for devlog - Extract and stream chat history to devlog server
168 lines (167 loc) • 6.03 kB
JavaScript
/**
* HTTP Client for DevLog ChatHub API
*
* Handles communication with the devlog server API endpoints,
* specifically for streaming chat data to the ChatHub service.
*/
import axios from 'axios';
export class DevlogApiClient {
client;
config;
constructor(config) {
this.config = {
timeout: 30000,
retries: 3,
retryDelay: 1000,
...config,
};
this.client = axios.create({
baseURL: this.config.baseURL,
timeout: this.config.timeout,
headers: {
'Content-Type': 'application/json',
},
});
// Add request/response interceptors for error handling
this.setupInterceptors();
}
setupInterceptors() {
// Request interceptor for logging
this.client.interceptors.request.use((config) => {
console.log(`[API] ${config.method?.toUpperCase()} ${config.url}`);
return config;
}, (error) => {
console.error('[API] Request error:', error);
return Promise.reject(error);
});
// Response interceptor for error handling and retries
this.client.interceptors.response.use((response) => {
console.log(`[API] ${response.status} ${response.config.url}`);
return response;
}, async (error) => {
const originalRequest = error.config;
// Don't retry if we've exceeded max retries
if (originalRequest._retryCount >= (this.config.retries || 3)) {
console.error('[API] Max retries exceeded:', error.message);
return Promise.reject(this.formatError(error));
}
// Retry on network errors or 5xx server errors
if (error.code === 'ECONNREFUSED' ||
error.code === 'ETIMEDOUT' ||
(error.response?.status && error.response.status >= 500)) {
originalRequest._retryCount = (originalRequest._retryCount || 0) + 1;
console.log(`[API] Retrying request (attempt ${originalRequest._retryCount})...`);
// Wait before retrying
await new Promise((resolve) => setTimeout(resolve, this.config.retryDelay * originalRequest._retryCount));
return this.client(originalRequest);
}
return Promise.reject(this.formatError(error));
});
}
formatError(error) {
if (error.response) {
// Server responded with error status
const message = error.response.data?.error || error.response.statusText;
return new Error(`API Error (${error.response.status}): ${message}`);
}
else if (error.request) {
// Request made but no response received
return new Error(`Network Error: Could not connect to server at ${this.config.baseURL}`);
}
else {
// Something else happened
return new Error(`Request Error: ${error.message}`);
}
}
/**
* Test connection to the devlog server
*/
async testConnection() {
try {
const response = await this.client.get('/api/health');
return response.status === 200;
}
catch (error) {
console.error('[API] Connection test failed:', error);
return false;
}
}
/**
* Import chat data to a workspace
*/
async importChatData(projectId, data) {
try {
const response = await this.client.post(`/api/projects/${projectId}/chat/import`, data);
return response.data;
}
catch (error) {
throw error instanceof Error ? error : new Error('Failed to import chat data');
}
}
/**
* Get import progress status
*/
async getImportProgress(projectId, importId) {
try {
const response = await this.client.get(`/api/projects/${projectId}/chat/import?importId=${importId}`);
return response.data;
}
catch (error) {
throw error instanceof Error ? error : new Error('Failed to get import progress');
}
}
/**
* List workspaces available on the server
*/
async listProjects() {
try {
const response = await this.client.get('/api/projects');
return response.data.workspaces || [];
}
catch (error) {
throw error instanceof Error ? error : new Error('Failed to list workspaces');
}
}
/**
* Get workspace details
*/
async getProject(projectId) {
try {
const response = await this.client.get(`/api/projects/${projectId}`);
return response.data;
}
catch (error) {
throw error instanceof Error ? error : new Error(`Failed to get workspace ${projectId}`);
}
}
/**
* Search chat content in a workspace
*/
async searchChatContent(projectId, query, options = {}) {
try {
const params = new URLSearchParams({
query,
limit: (options.limit || 50).toString(),
caseSensitive: (options.caseSensitive || false).toString(),
searchType: options.searchType || 'exact',
});
const response = await this.client.get(`/api/projects/${projectId}/chat/search?${params.toString()}`);
return response.data;
}
catch (error) {
throw error instanceof Error ? error : new Error('Failed to search chat content');
}
}
/**
* Get chat statistics for a workspace
*/
async getChatStats(projectId) {
try {
const response = await this.client.get(`/api/projects/${projectId}/chat/stats`);
return response.data;
}
catch (error) {
throw error instanceof Error ? error : new Error('Failed to get chat statistics');
}
}
}