Package Exports
- solver-sdk
Readme
Code Solver SDK
AI-powered code analysis SDK with WorkCoins billing system. 100% typed, production ready.
Install
npm install solver-sdk@9.0.8
# или
npm install solver-sdk@latestQuick Start
import { CodeSolverSDK } from 'solver-sdk';
const sdk = await CodeSolverSDK.create({
baseURL: 'https://api.example.com',
getAuthToken: async () => getToken()
});
// Projects
const projects = await sdk.projects.getAllProjects();
await sdk.projects.createProject('MyProject');
// Chat with Thinking
const response = await sdk.chat.chat([
{ role: 'user', content: 'explain this code' }
], { thinking: { type: 'enabled', budget_tokens: 10000 } });
// Search
const results = await sdk.search.searchCode({
projectId,
query: 'authentication function'
});
// User & Updates
const profile = await sdk.user.getProfile();
const update = await sdk.updates.checkForUpdates({...});API Overview
| Feature | Methods | Status |
|---|---|---|
| Projects | 19 | ✅ Full CRUD + state |
| Delta Chunking | 7 | ✅ Encrypted sync |
| Chat | 13 | ✅ Streaming + Extended Thinking |
| Search | 4 | ✅ Vector-based |
| Tools | 5 | ✅ AI tool schemas |
| Models | 5 | ✅ Provider info |
| Updates | 5 | ✅ Auto-check + changelog |
| User | 3 | ✅ Profile + Limits |
| Credits | 3 | ✅ Balance + Status + Estimates |
| Auth | 2 | ✅ Token revocation + logout |
Total: 66 methods | Full API Reference
Quick Reference
Projects (19) - управление проектами и индексация
getAllProjects() | getProject(id) | createProject(name) | deleteProject(id)
findOrCreateProject(name) | getReadyProjects() | getProjectState(id)
getProjectStatus(id) | getIndexingStatus(id) | cancelIndexing(id)
clearIndexingError(id) | getRecoveryStatus(id) | resumeSync(id)
cancelRecovery(id) | initializeDeltaSync(id) | resetIndexing(id)
restartIndexing(id) | getFilePathMapping(id)Chat (13) - AI диалоги и streaming
chat(messages, options?) | chatCompletion(messages, options?)
chatWithRegionFailover(messages, options?) | streamChat(messages, options?)
streamPrompt(prompt, options?) | sendContinuation(requestId, messages)
resumeChat(requestId, messages, partialText, options?)
sendPromptWithRegionFailover(prompt, options?) | checkAvailability()
cancelRequest(requestId) | getStreamsStats() | cleanupStaleStreams()
cancelUserStreams(reason?)Delta Chunking (7) - синхронизация файлов
initSync(projectId, request) | uploadChunkBatch(projectId, chunks)
uploadChunksWithRetry(projectId, chunks, options?)
finalizeSync(projectId) | getSyncStatus(projectId)
cancelSync(projectId) | cleanupFiles(projectId, activeFiles)Search (4) - поиск кода
searchCode(projectId, params) | searchFunctions(projectId, params)
semanticSearch(projectId, params) | getFunctionStats(projectId)Tools (5) - AI инструменты
getSchemas() | findToolByName(name) | getToolsStats()
validateToolSchema(tool) | createToolSchema(name, desc, props, required?)Models (5) - AI модели
getAllModels() | getModels() | getAvailableModels()
getProviderModels(providerId) | getModelInfo(modelId)Updates (5) - обновления SDK
checkForUpdates(options) | getChangelog(version, locale)
sendStats(event) | getLatestVersion(channel?) | checkAvailability()Credits (3) - WorkCoins баланс
getBalance() | getStatus() | estimate(tokens, model?, operationType?)User (3) - профиль и лимиты
getProfile() | getLimitStatus() | checkAvailability()Auth (2) - аутентификация
revokeToken(token) | logout()Auth
// Local dev
const sdk = await CodeSolverSDK.create({
baseURL: 'http://localhost:3000',
getAuthToken: async () => 'your-token'
});
// Production with token refresh + error handling
import { AuthenticationError } from 'solver-sdk';
const sdk = await CodeSolverSDK.create({
baseURL: 'https://api.example.com',
getAuthToken: async () => {
if (isTokenExpired()) {
await refreshToken();
}
return getToken();
}
});
// All methods throw AuthenticationError on 401
try {
await sdk.user.getProfile();
} catch (error) {
if (error instanceof AuthenticationError) {
await refreshToken();
// retry
}
}WebSocket (Real-time)
// Connect
await sdk.connectWebSocket();
// Subscribe to project
sdk.projectSync.subscribeToProject(projectId);
// Listen for updates
sdk.projectSync.on('sync-progress', (data) => {
console.log(`Progress: ${data.processedFiles}/${data.totalFiles}`);
});
sdk.projectSync.on('sync-completed', (data) => {
console.log(`Done: ${data.statistics.totalFiles} files`);
});Error Handling
All SDK methods throw typed exceptions. All errors have: statusCode, errorType, message, requestId, timestamp.
Available Error Types
import {
AuthenticationError, // 401 - token expired/invalid
ForbiddenError, // 403 - access denied
ValidationError, // 422 - invalid data
NotFoundError, // 404 - resource not found
ConflictError, // 409 - data conflict
BadRequestError, // 400 - bad request
InternalServerError, // 500 - server error
ServiceUnavailableError,// 503 - service down
GatewayTimeoutError, // 504 - timeout
LimitExceededError, // 403 - WorkCoins limit exceeded
RateLimitError, // 429 - too many requests
NetworkError, // 0 - network failure
TimeoutError, // 408 - request timeout
DatabaseError // 400 - database error
} from 'solver-sdk';Common Patterns
import { AuthenticationError, LimitExceededError, RateLimitError } from 'solver-sdk';
try {
await sdk.chat.streamChat([{ role: 'user', content: 'Hello' }]);
} catch (error) {
// Auth errors - refresh token and retry
if (error instanceof AuthenticationError) {
await refreshToken();
return retry();
}
// WorkCoins limit - show upgrade prompt
if (error instanceof LimitExceededError) {
console.log(`WorkCoins exhausted: ${error.message}`);
if (error.action) {
console.log(`Recommendation: ${error.action.suggestion}`);
console.log(`Action URL: ${error.action.url}`);
}
}
// Rate limit - wait and retry
if (error instanceof RateLimitError) {
await new Promise(r => setTimeout(r, error.getRetryDelayMs()));
return retry();
}
}Proactive Limit Checking
// Check WorkCoins before expensive operations
const status = await sdk.credits.getStatus();
if (!status.canMakeRequest) {
console.error('No WorkCoins available');
return;
}
if (status.status === 'warning') {
console.warn(`Low on WorkCoins: ${status.message}`);
}
// Make request
await sdk.chat.streamChat([...]);WorkCoins (Credits) API
// Get balance
const balance = await sdk.credits.getBalance();
console.log(`${balance.creditsRemaining} / ${balance.creditsLimit} WorkCoins`);
// Get status with recommendations
const status = await sdk.credits.getStatus();
console.log(`Status: ${status.status}`); // 'ok' | 'warning' | 'critical' | 'depleted'
// Estimate cost
const estimate = await sdk.credits.estimate(50000, 'haiku', 'code_generation');
console.log(`Estimated: ${estimate.estimatedCredits} WorkCoins`);Streaming Tool Use (Создание/Редактирование файлов)
SDK передаёт только официальные события Anthropic API через callback onEvent. Когда AI создаёт или редактирует файлы, параметры инструментов стримятся посимвольно через официальное событие input_json_delta.
✅ Рекомендация Anthropic: Используйте стандартное событие
content_block_deltaс типомinput_json_deltaдля real-time визуализации прогресса. Это обеспечивает совместимость с обновлениями API и гарантирует стабильность работы.
Real-time отображение прогресса через официальный API
// Накапливаем partial JSON для каждого инструмента
const toolInputs = new Map<number, string>();
for await (const chunk of sdk.chat.streamChat(messages, {
projectId: 'project-id',
tools: ['read_file', 'search_replace'],
onEvent: (type, data) => {
// 🎯 Стриминг параметров инструмента
if (type === 'content_block_delta' && data.delta?.type === 'input_json_delta') {
const index = data.index;
const partialJson = data.delta.partial_json;
// Накапливаем JSON по индексу блока
const accumulated = (toolInputs.get(index) || '') + partialJson;
toolInputs.set(index, accumulated);
// Пытаемся извлечь читаемые поля из неполного JSON (безопасно)
try {
const filePathMatch = accumulated.match(/"file_path"\s*:\s*"([^"]*)"/);
const operationMatch = accumulated.match(/"operation"\s*:\s*"([^"]*)"/);
if (filePathMatch) {
// 💡 Показываем пользователю: "Редактирование: src/app.ts"
console.log(`📝 ${operationMatch?.[1] || 'Обработка'}: ${filePathMatch[1]}`);
}
// Обновляем UI с прогрессом
updateProgress({
file: filePathMatch?.[1],
operation: operationMatch?.[1],
bytesReceived: accumulated.length
});
} catch {
// JSON ещё неполный - ждём следующий чанк
}
}
// ✅ Инструмент готов к выполнению (полный JSON получен)
if (type === 'content_block_stop') {
const index = data.index;
const fullJson = toolInputs.get(index);
if (fullJson) {
try {
const toolInput = JSON.parse(fullJson);
console.log('🔧 Инструмент готов:', toolInput);
toolInputs.delete(index); // Очищаем
} catch (e) {
console.error('❌ Некорректный JSON инструмента');
}
}
}
}
})) {
// Обрабатываем остальные события...
}Официальные события Anthropic API
| Событие | Данные | Описание |
|---|---|---|
message_start |
{ message: {...} } |
Начало ответа AI |
content_block_start |
{ index, content_block } |
Начало блока (text/thinking/tool_use) |
content_block_delta |
{ index, delta } |
Стриминг содержимого |
content_block_stop |
{ index } |
Завершение блока |
message_delta |
{ delta } |
Метаданные ответа |
message_stop |
{} |
Конец ответа |
Типы delta для content_block_delta
// Текст ответа AI
delta: { type: 'text_delta', text: 'Создаю функцию...' }
// Мышление AI (Extended Thinking)
delta: { type: 'thinking_delta', thinking: 'Анализирую требования...' }
// 🎯 Параметры инструмента (посимвольно!)
delta: { type: 'input_json_delta', partial_json: '{"file_path": "src/app' }Пример UI компонента
function ToolProgressIndicator() {
const [currentTool, setCurrentTool] = useState<{
file?: string;
operation?: string;
progress: number;
} | null>(null);
// В onEvent callback
if (type === 'content_block_delta' && data.delta?.type === 'input_json_delta') {
// Извлекаем инфо из partial JSON
const fileMatch = accumulated.match(/"file_path"\s*:\s*"([^"]*)"/);
const opMatch = accumulated.match(/"operation"\s*:\s*"([^"]*)"/);
setCurrentTool({
file: fileMatch?.[1],
operation: opMatch?.[1] || 'processing',
progress: accumulated.length
});
}
return currentTool && (
<div className="tool-progress">
<span className="operation">{currentTool.operation}</span>
<span className="file">{currentTool.file}</span>
<div className="progress-bar" style={{ width: `${currentTool.progress / 10}%` }} />
</div>
);
}Fine-grained Tool Streaming
SDK автоматически использует fine-grained tool streaming для всех запросов с tools. Это значительно снижает latency при генерации больших tool inputs:
- Без fine-grained: задержки 15+ секунд, мелкие чанки
- С fine-grained: задержки ~3 секунды, крупные чанки
Обработка Invalid JSON
При использовании fine-grained streaming возможны incomplete JSON responses (особенно при достижении max_tokens). SDK автоматически:
- Обнаруживает invalid JSON в tool responses
- Оборачивает в валидный объект
{"INVALID_JSON": "..."} - Отправляет retry запрос с увеличенным max_tokens
Метрики доступны в DEBUG режиме логирования.
Types
import {
ChatMessage,
ChatOptions,
Project,
ProjectState,
SearchResult,
UserProfile,
LimitStatus,
LimitExceededError,
RateLimitError
} from 'solver-sdk';Debug
const sdk = await CodeSolverSDK.create({
debug: 'verbose', // silent | error | warn | info | debug | verbose
webSocket: { debug: true }
});Delta Chunking (Indexing)
// ✅ Main indexing method
await sdk.deltaManager.syncEncryptedChunks(projectId, chunks, rootHash, {
batchSize: 50,
onProgress: (current, total) => console.log(`${current}/${total}`)
});
// Check sync status
const state = await sdk.projects.getProjectState(projectId, clientRootHash);
if (state.syncRequired) {
// Needs sync
}
// Cancel ongoing sync
await sdk.deltaManager.cancelSync(projectId);File Invalidation
// Инвалидировать файл при изменении
const result = await sdk.deltaManager.invalidateFile(projectId, {
filePath: 'src/app.tsx',
reason: 'file_changed'
});
console.log(`Invalidated ${result.chunksInvalidated} chunks`);
console.log(`Cache invalidated: ${result.cacheInvalidated}`);
// Отправить новые chunks после инвалидации
await sdk.deltaManager.syncEncryptedChunks(projectId, newChunks, rootHash);Session Recovery
// Always check on project open
const recovery = await sdk.projects.getRecoveryStatus(projectId);
if (recovery.needsRecovery) {
const { processedFiles, totalFiles, percentage } = recovery.progress;
// Show dialog: "Continue (67%) — 480/642 files" or "Start Fresh"
await sdk.projects.resumeSync(projectId); // Continue
// OR
await sdk.projects.cancelRecovery(projectId); // Start over
}Key Types
ProjectState
interface ProjectState {
projectId: string;
merkleRootHash: string | null;
totalFiles: number; // For UI display
indexingStatus: 'pending' | 'in-progress' | 'complete' | 'failed';
syncRequired?: boolean; // Compare with client hash
}SyncProgressEvent
interface SyncProgressEvent {
projectId: string;
progress: number; // 0-100
processedFiles: number; // 450
totalFiles: number; // 722
stage: 'receiving_chunks' | 'processing_chunks' | 'creating_embeddings' | 'finalizing';
}RecoveryInfo
interface RecoveryInfo {
needsRecovery: boolean;
progress: {
processedFiles: number;
totalFiles: number;
percentage: number; // 0-100
};
canResume: boolean;
}CreditsStatus
interface CreditsStatus {
status: 'ok' | 'warning' | 'critical' | 'depleted';
balance: CreditsBalance;
canMakeRequest: boolean;
message: string;
action?: CreditsAction;
}CreditsBalance
interface CreditsBalance {
creditsLimit: number; // Monthly credits limit
creditsUsed: number; // Total credits used
creditsRemaining: number; // Credits remaining
percentUsed: number; // Usage percentage (0-100)
bonusCredits?: number; // Bonus credits available
bonusCreditsUsed?: number; // Bonus credits used
bonusCreditsRemaining?: number; // Bonus credits remaining
autoPurchaseEnabled: boolean; // Is auto-purchase enabled
purchasedCredits?: number; // Credits purchased this month via auto-purchase
resetDate?: Date; // Monthly reset date
}CreditsAction
interface CreditsAction {
type: 'info' | 'upgrade' | 'enable_auto_purchase' | 'buy_credits';
suggestion: string; // User-facing recommendation
url: string; // Action URL (e.g. '/dashboard#billing')
options?: string[]; // Available options ['enable_auto_purchase', 'buy_credits', 'upgrade']
}Troubleshooting
"0 files" after indexing
const state = await sdk.projects.getProjectState(projectId);
console.log(state.totalFiles); // Should be > 0
// If 0: update backend to latest versionNo WebSocket updates
// 1. Check connection
console.log(sdk.isWebSocketConnected); // should be true
// 2. Verify subscription
sdk.projectSync.subscribeToProject(projectId);
// 3. Ensure OAuth token (production)
const sdk = await CodeSolverSDK.create({
getAuthToken: () => authManager.getAccessToken()
});
// 4. Fallback to HTTP polling
const status = await sdk.projects.getIndexingStatus(projectId);Interrupted sync
const recovery = await sdk.projects.getRecoveryStatus(projectId);
if (recovery.needsRecovery) {
// Option 1: Resume
await sdk.projects.resumeSync(projectId);
// Option 2: Cancel and restart
await sdk.projects.cancelRecovery(projectId);
}API Reference
Projects (sdk.projects)
getAllProjects(): Promise<Project[]>
getProjects(): Promise<Project[]>
getProject(projectId: string): Promise<Project>
createProject(name: string, data?: any, options?: ProjectOptions): Promise<Project>
findOrCreateProject(name: string): Promise<Project>
deleteProject(projectId: string): Promise<void>
getReadyProjects(): Promise<Project[]>
getProjectState(projectId: string, clientRootHash?: string): Promise<ProjectState>
getProjectStatus(projectId: string): Promise<any>
getIndexingStatus(projectId: string): Promise<any>
cancelIndexing(projectId: string): Promise<boolean>
clearIndexingError(projectId: string): Promise<boolean>
getRecoveryStatus(projectId: string): Promise<RecoveryInfo>
resumeSync(projectId: string, options?: any): Promise<any>
cancelRecovery(projectId: string): Promise<void>
initializeDeltaSync(projectId: string, params: any): Promise<any>
resetIndexing(projectId: string): Promise<any>
restartIndexing(projectId: string): Promise<any>
getFilePathMapping(projectId: string): Promise<any>Delta Manager (sdk.deltaManager)
initSync(projectId: string, request: SyncInitRequest): Promise<any>
uploadChunkBatch(projectId: string, chunks: any[]): Promise<any>
uploadChunksWithRetry(projectId: string, chunks: any[], options?: any): Promise<any>
finalizeSync(projectId: string): Promise<SyncResult>
getSyncStatus(projectId: string): Promise<SyncStatus>
cancelSync(projectId: string): Promise<{ success: boolean; message?: string }>
cleanupFiles(projectId: string, activeFiles: string[]): Promise<any>Chat (sdk.chat)
chat(messages: ChatMessage[], options?: ChatOptions): Promise<ChatResponse>
chatCompletion(messages: ChatMessage[], options?: ChatOptions): Promise<ChatResponse>
chatWithRegionFailover(messages: ChatMessage[], options?: ChatOptions): Promise<ChatResponse>
streamChat(messages: ChatMessage[], options?: ChatStreamOptions): AsyncGenerator<ChatStreamChunk>
streamPrompt(prompt: string, options?: ChatStreamOptions): AsyncGenerator<ChatStreamChunk>
sendContinuation(requestId: string, messages: ChatMessage[]): AsyncGenerator<ChatStreamChunk>
resumeChat(requestId: string, messages: ChatMessage[], partialText: string, options?: ChatStreamOptions): AsyncGenerator<ChatStreamChunk>
sendPromptWithRegionFailover(prompt: string, options?: ChatOptions): Promise<string>
checkAvailability(): Promise<boolean>
cancelRequest(requestId: string): Promise<void>
getStreamsStats(): Promise<any>
cleanupStaleStreams(timeoutMs?: number): Promise<any>
cancelUserStreams(reason?: string): Promise<number>Search (sdk.search)
searchCode(projectIdOrParams: string | SearchCodeParams, params?: SearchCodeParams): Promise<SearchResult[]>
searchFunctions(projectIdOrParams: string | SearchFunctionsParams, params?: SearchFunctionsParams): Promise<FunctionSearchResult>
semanticSearch(projectId: string, params: SearchCodeParams): Promise<SearchResult[]>
getFunctionStats(projectId: string): Promise<{ stats: { totalFunctions: number } }>Tools (sdk.tools)
getSchemas(): Promise<ToolsResponse>
findToolByName(name: string): Promise<ToolSchema | null>
getToolsStats(): Promise<{ total: number; categories: Record<string, number>; mostUsed?: string[] }>
validateToolSchema(tool: ToolSchema): boolean
createToolSchema(name: string, description: string, properties: any, required?: string[]): ToolSchemaUser (sdk.user)
getProfile(): Promise<UserProfile>
getLimitStatus(): Promise<LimitStatus>
checkAvailability(): Promise<boolean>Credits (sdk.credits)
getBalance(): Promise<CreditsBalance>
getStatus(): Promise<CreditsStatus>
estimate(tokens: number, model?: ModelType, operationType?: OperationType): Promise<CreditsEstimate>Auth (sdk.auth)
revokeToken(token: string): Promise<{ ok: boolean }>
logout(): Promise<{ ok: boolean }>Models (sdk.models)
getAllModels(): Promise<any[]>
getModels(): Promise<any[]>
getAvailableModels(): Promise<any[]>
getProviderModels(providerId: string): Promise<ProviderModels>
getModelInfo(modelId: string): Promise<any>Updates (sdk.updates)
checkForUpdates(options: UpdateCheckOptions): Promise<UpdateResponse>
getChangelog(version: string, locale: string): Promise<string>
sendStats(event: UpdateStatsEvent): Promise<void>
getLatestVersion(channel?: string): Promise<LatestVersionInfo>
checkAvailability(): Promise<boolean>WebSocket (sdk.projectSync)
connectWebSocket(): Promise<void>
disconnectWebSocket(): void
isWebSocketConnected: boolean
projectSync.subscribeToProject(projectId: string): void
projectSync.unsubscribeFromProject(projectId: string): void
projectSync.on('sync-status-update', callback): void
projectSync.on('sync-progress', callback): void
projectSync.on('sync-completed', callback): void
projectSync.on('sync-error', callback): void
projectSync.off(event: string, callback): voidUtility
checkHealth(): Promise<boolean>
version: string
baseURL: stringDocumentation
All methods have full JSDoc comments with examples. Check your IDE autocomplete for detailed reference.
For full API documentation: See API Reference section above.