JSPM

  • Created
  • Published
  • Downloads 14
  • Score
    100M100P100Q82171F
  • License MIT

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

Package Exports

  • solver-sdk

Readme

Code Solver SDK v8.1.0

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

Install

npm install solver-sdk@8.1.0

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
const sdk = await CodeSolverSDK.create({
  baseURL: 'https://api.example.com',
  getAuthToken: async () => {
    if (isTokenExpired()) {
      await refreshToken();
    }
    return getToken();
  }
});

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

Auth Errors

By default, 401/403 auth errors return null instead of throwing:

const projects = await sdk.projects.getAllProjects();
if (!projects) {
  // Token expired - refresh and retry
  await refreshToken();
}

// To throw errors instead:
const sdk = await CodeSolverSDK.create({
  suppressAuthErrors: false
});

Usage Limits & Rate Limiting

SDK automatically handles usage limit and rate limit errors with specialized error classes:

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('Usage limit exceeded!');
    console.log(`Type: ${error.statusType}`); // e.g. 'debt_limit_exceeded'
    console.log(`Blocked: ${error.isBlocked()}`); // true/false
    
    if (error.action) {
      console.log(`Action: ${error.action.suggestion}`);
      console.log(`URL: ${error.getActionUrl()}`);
    }
    
    if (error.balance) {
      console.log(`Balance: ${error.balance.currentRub}`);
      console.log(`Required topup: ${error.balance.requiredTopup}`);
    }
  }
}

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);

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;
}

LimitStatus

interface LimitStatus {
  status: 'ok' | 'warning' | 'critical' | 'blocked' | 'on_demand';
  statusType: string;              // e.g. 'using_bonus_tokens', 'debt_limit_exceeded'
  message: string;                 // Human-readable message
  canMakeRequest: boolean;         // Can user make API requests?
  
  currentSource?: 'bonus' | 'subscription' | 'on_demand';
  
  remaining?: {
    bonusTokens?: number;          // Remaining bonus tokens
    bonusPercent?: number;         // % remaining
    subscriptionTokens?: number;   // Remaining subscription tokens
    subscriptionPercent?: number;
  };
  
  balance?: {
    currentRub?: number;           // Current balance in rubles
    maxDebtRub?: number;           // Max allowed debt
    requiredTopup?: number;        // Required topup amount
  };
  
  action?: {
    type: 'info' | 'urgent' | 'required';
    suggestion: string;            // "Top up balance" / "Upgrade plan"
    ctaUrl?: string;               // Dashboard URL
  };
}

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