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 v8.3.1

AI-powered code analysis SDK with WorkCoins billing system. 100% typed, production ready.

Install

npm install solver-sdk@8.3.1

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 12 ✅ 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 7 ✅ Balance + Overage management
Auth 2 ✅ Token revocation + 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 instead of returning null. This enables better error handling with TypeScript autocomplete and proper error recovery.

Available Error Types

import { 
  // Auth errors
  AuthenticationError,    // 401 - token expired/invalid
  ForbiddenError,        // 403 - access denied
  
  // Client errors  
  ValidationError,       // 422 - invalid data
  NotFoundError,        // 404 - resource not found
  ConflictError,        // 409 - data conflict
  BadRequestError,      // 400 - bad request
  
  // Server errors
  InternalServerError,   // 500 - server error
  ServiceUnavailableError, // 503 - service down
  GatewayTimeoutError,   // 504 - timeout
  
  // Special errors
  LimitExceededError,    // 403 - credits limit exceeded
  RateLimitError,        // 429 - too many requests
  NetworkError,          // 0 - network failure
  TimeoutError,          // 408 - request timeout
  DatabaseError          // 400 - database error
} from 'solver-sdk';

Basic Error Handling

try {
  const status = await sdk.credits.getStatus();
  console.log(status.balance);
  
} catch (error) {
  // All errors have common properties:
  console.log(error.statusCode);   // HTTP status code
  console.log(error.errorType);    // Error type string
  console.log(error.requestId);    // For correlation with backend logs
  console.log(error.timestamp);    // When error occurred
  console.log(error.rawResponse);  // Full backend response
}

Auth Error Handling

import { AuthenticationError } from 'solver-sdk';

try {
  const balance = await sdk.credits.getBalance();
} catch (error) {
  if (error instanceof AuthenticationError) {
    // Token expired or invalid
    console.log('Token expired, refreshing...');
    await refreshToken();
    // Retry the request
    return retry();
  }
}

Usage Limits & Rate Limiting

SDK provides specialized error classes for limits:

Checking Limit Status

import { CodeSolverSDK } from 'solver-sdk';

const sdk = await CodeSolverSDK.create({ /* ... */ });

// Get detailed limit status
const status = await sdk.user.getLimitStatus();

console.log(`Status: ${status.status}`); // 'ok' | 'warning' | 'critical' | 'blocked' | 'on_demand'
console.log(`Can make requests: ${status.canMakeRequest}`);

if (status.status === 'warning' || status.status === 'critical') {
  console.warn(`Warning: ${status.message}`);
}

if (status.action) {
  console.log(`Recommendation: ${status.action.suggestion}`);
}

Handling Limit Exceeded (403)

import { CodeSolverSDK, LimitExceededError } from 'solver-sdk';

try {
  await sdk.chat.streamChat([
    { role: 'user', content: 'Hello' }
  ]);
} catch (error) {
  if (error instanceof LimitExceededError) {
    console.log('WorkCoins exceeded!');
    console.log(`Message: ${error.message}`); // 'WorkCoins исчерпаны. Купите тариф...'
    console.log(`Blocked: ${error.isBlocked()}`); // true/false
    
    // Action recommendation
    if (error.action) {
      console.log(`Action: ${error.action.type}`); // 'upgrade' | 'enable_overage'
      console.log(`Suggestion: ${error.action.suggestion}`);
      console.log(`URL: ${error.action.url}`);
    }
    
    // Balance details
    if (error.balance) {
      console.log(`Used: ${error.balance.percentUsed}%`);
      console.log(`Credits: ${error.balance.creditsUsed}/${error.balance.creditsLimit}`);
    }
  }
}

Handling Rate Limit (429)

import { CodeSolverSDK, RateLimitError } from 'solver-sdk';

try {
  // Making multiple requests rapidly
  for (let i = 0; i < 100; i++) {
    await sdk.user.getProfile();
  }
} catch (error) {
  if (error instanceof RateLimitError) {
    console.log('Rate limit exceeded!');
    console.log(`Wait ${error.retryAfter} seconds`);
    console.log(`Retry at: ${error.retryAt}`);
    console.log(`Time remaining: ${error.getRetryDelayMs()}ms`);
    
    // Wait and retry
    await new Promise(resolve => setTimeout(resolve, error.getRetryDelayMs()));
    // ... retry request
  }
}

Proactive Limit Checking

// Check before making expensive operations
async function sendChatMessage(message: string) {
  // 1. Check limit status first
  const status = await sdk.user.getLimitStatus();
  
  if (!status.canMakeRequest) {
    console.error('Cannot make request:', status.message);
    return;
  }
  
  if (status.status === 'warning') {
    console.warn('Low on tokens:', status.message);
  }
  
  // 2. Make the request
  try {
    await sdk.chat.streamChat([
      { role: 'user', content: message }
    ]);
  } catch (error) {
    if (error instanceof LimitExceededError) {
      // Handle limit error
    } else if (error instanceof RateLimitError) {
      // Handle rate limit
    }
  }
}

Credits System (WorkCoins)

Note: In the WorkAI UI, this system is referred to as "WorkCoins" for user clarity. The API and SDK continue to use credits in method names and responses for backward compatibility.

SDK provides full support for WorkCoins-based pricing model:

Check WorkCoins Balance

const balance = await sdk.credits.getBalance();

console.log(`WorkCoins limit: ${balance.creditsLimit}`);
console.log(`WorkCoins used: ${balance.creditsUsed}`);
console.log(`WorkCoins remaining: ${balance.creditsRemaining}`);
console.log(`Usage: ${balance.percentUsed}%`);

if (balance.bonusCredits) {
  console.log(`Bonus WorkCoins: ${balance.bonusCredits}`);
}

if (balance.overageEnabled) {
  console.log(`Overage WorkCoins used: ${balance.overageCreditsUsed}/${balance.overageLimit}`);
}

Check WorkCoins Status (with recommendations)

const status = await sdk.credits.getStatus();

console.log(`Status: ${status.status}`); // 'ok' | 'warning' | 'critical' | 'depleted'
console.log(`Can make request: ${status.canMakeRequest}`);
console.log(`Message: ${status.message}`);

if (status.action) {
  console.log(`Action: ${status.action.type}`); // 'info' | 'upgrade' | 'enable_overage'
  console.log(`Suggestion: ${status.action.suggestion}`);
  console.log(`URL: ${status.action.url}`);
}

Estimate Operation Cost (WorkCoins)

// Estimate before making a request
const estimate = await sdk.credits.estimate(
  50000,    // tokens (technical parameter for estimation)
  'haiku',  // model: 'haiku' | 'sonnet' | 'auto'
  'code_generation' // operation type
);

console.log(`Estimated WorkCoins: ${estimate.estimatedCredits}`);
console.log(`Confidence: ${estimate.confidence}`); // 'low' | 'medium' | 'high'
console.log(`Breakdown:`, estimate.breakdown);

Manage Overage (Pay-as-you-go)

// Get overage config
const config = await sdk.credits.getOverageConfig();
console.log(`Enabled: ${config.enabled}`);
console.log(`Limit: ${config.limitCredits} WorkCoins`);
console.log(`Price: ₽${config.pricePerCreditRub} per WorkCoin`);

// Enable/disable overage
await sdk.credits.toggleOverage(true);

// Update overage limit
await sdk.credits.updateOverageLimit(1000);

// Get overage stats for current month
const stats = await sdk.credits.getOverageStats();
console.log(`Total WorkCoins used: ${stats.totalCreditsUsed}`);
console.log(`Total cost: ₽${stats.totalCostRub}`);
console.log(`Usage count: ${stats.usageCount}`);
console.log(`Avg per operation: ${stats.avgCreditsPerOperation}`);

Handle WorkCoins Errors

import { CodeSolverSDK, CreditsStatus } from 'solver-sdk';

try {
  // Check before making request
  const status = await sdk.credits.getStatus();
  if (!status.canMakeRequest) {
    console.error('No WorkCoins available');
    return;
  }

  // Make AI request
  await sdk.chat.chat([...]);
} catch (error) {
  if (error.response?.data?.error === 'credits_limit_exceeded') {
    console.error('WorkCoins exhausted. Please upgrade or enable overage.');
  } else if (error.response?.data?.error === 'overage_limit_exceeded') {
    console.error('Overage limit reached.');
  }
}

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
  
  overageEnabled: boolean;         // Is auto-renewal enabled
  overageCreditsUsed?: number;     // Auto-renewal credits used
  overageLimit?: number;           // Auto-renewal limit
  overageRemaining?: number;       // Auto-renewal credits remaining
  
  resetDate?: Date;                // Monthly reset date
}

CreditsAction

interface CreditsAction {
  type: 'info' | 'upgrade' | 'enable_overage';
  suggestion: string;              // User-facing recommendation
  url: string;                     // Action URL (e.g. '/dashboard#billing')
  options?: string[];              // Available options ['enable_overage', '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>
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>

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

Docs

All methods have JSDoc comments - check IDE autocomplete for full reference.


Status: ✅ Production Ready | v8.1.0 | License: MIT