JSPM

  • Created
  • Published
  • Downloads 11
  • Score
    100M100P100Q82316F
  • License MIT

SDK for WorkAI API - AI-powered code analysis with WorkCoins billing system

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@latest

Quick 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 автоматически:

  1. Обнаруживает invalid JSON в tool responses
  2. Оборачивает в валидный объект {"INVALID_JSON": "..."}
  3. Отправляет 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 version

No 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[]): ToolSchema

User (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): void

Utility

checkHealth(): Promise<boolean>
version: string
baseURL: string

Documentation

All methods have full JSDoc comments with examples. Check your IDE autocomplete for detailed reference.

For full API documentation: See API Reference section above.