JSPM

androidsprintteam-solver-sdk

7.1.1
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 7
  • Score
    100M100P100Q56339F
  • License MIT

SDK for API integration (Pure Cloud-First - Delta Chunking only)

Package Exports

  • androidsprintteam-solver-sdk

Readme

Code Solver SDK v7.1.1

Backend SDK для интеграции с Code Solver API (Pure Cloud-First)

📑 Навигация

Быстрый старт:

Работа с проектами:

Типы и решение проблем:


🔒 Установка (GitHub Packages - Private)

SDK теперь приватный и публикуется в GitHub Packages.

Требования:

  • GitHub Personal Access Token с правами read:packages
  • Доступ к организации AndroidSprintTeam

Установка:

# 1. Создай .npmrc в корне проекта
cat > .npmrc << 'EOF'
//npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKEN
@androidsprintteam:registry=https://npm.pkg.github.com
always-auth=true
EOF

# 2. Установи SDK
npm install @androidsprintteam/solver-sdk

Создание токена: https://github.com/settings/tokens/new (scopes: read:packages, repo)

Полная документация: см. GITHUB_PACKAGES_SETUP.md

🚀 Быстрый старт

Локальная разработка

import { CodeSolverSDK } from '@androidsprintteam/solver-sdk';

const sdk = await CodeSolverSDK.create({
  baseURL: 'http://localhost:3000',
  apiKey: 'your-api-key'
});

Production с OAuth

const sdk = await CodeSolverSDK.create({
  baseURL: 'https://workai.su/api/v1',
  headers: { Authorization: `Bearer ${oauth_token}` },
  webSocket: { enabled: true }
});

🔑 Динамическое обновление токена (v6.1.4+)

const sdk = await CodeSolverSDK.create({
  baseURL: 'https://workai.su/api/v1',
  getAuthToken: async () => {
    if (authManager.isTokenExpired()) {
      await authManager.refreshAccessToken();
    }
    return authManager.getAccessToken();
  }
});

📖 Быстрая справка

Задача Метод Назначение
Индексация
Синхронизировать проект sdk.deltaManager.syncEncryptedChunks(projectId, chunks, rootHash) Единственный способ индексации
Проверить статус sdk.projects.getProjectState(projectId, clientRootHash?) Получить состояние + totalFiles
Проверить recovery sdk.projects.getRecoveryStatus(projectId) Нужно ли возобновить прерванную синхронизацию
Возобновить синхронизацию sdk.projects.resumeSync(projectId) Продолжить прерванную индексацию
Real-time прогресс
Подключить WebSocket await sdk.connectWebSocket() Для live обновлений
Подписаться на проект sdk.projectSync.subscribeToProject(projectId) Получать события по проекту
Прогресс индексации sdk.projectSync.on('sync-progress', callback) data.processedFiles / data.totalFiles
Завершение индексации sdk.projectSync.on('sync-completed', callback) data.statistics.totalFiles
Проекты
Получить все проекты sdk.projects.getAllProjects() Список всех проектов
Найти или создать sdk.projects.findOrCreateProject(name) Idempotent создание
Получить проект sdk.projects.getProject(projectId) Детали проекта
Поиск
Поиск по коду sdk.search.searchCode(projectId, query) Semantic search с reranker
Поиск функций sdk.search.searchFunctions(projectId, query) Только функции/методы
Чат
Streaming чат sdk.chat.streamChat(messages, options) AsyncGenerator chunks
Обычный чат sdk.chat.chat(messages, options) Promise response

📖 API Reference (подробно)

Projects API (sdk.projects) - управление проектами
// Основные методы
getAllProjects(): Promise<Project[]>
getProject(projectId: string): Promise<Project>
findOrCreateProject(projectName: string): Promise<Project>

// Индексация
getProjectState(projectId: string, clientRootHash?: string): Promise<ProjectState>
getIndexingStatus(projectId: string): Promise<any>

// Recovery
getRecoveryStatus(projectId: string): Promise<any>
resumeSync(projectId: string, options?: any): Promise<any>
cancelRecovery(projectId: string): Promise<any>
Delta Manager API (sdk.deltaManager) - индексация проекта
// Основной метод индексации
syncEncryptedChunks(
  projectId: string, 
  encryptedChunks: any[], 
  rootHash: string, 
  options?: { batchSize?: number; onProgress?: (current, total) => void }
): Promise<any>

// Вспомогательные
getSyncStatus(projectId: string): Promise<SyncStatus>
cancelSync(projectId: string): Promise<boolean>
cleanupDeletedFiles(projectId: string, activeFiles: any[]): Promise<any>
Search API (sdk.search) - поиск по коду
searchCode(projectId: string, query: string, options?: any): Promise<any[]>
searchFunctions(projectId: string, query: string, options?: any): Promise<any[]>
semanticSearch(projectId: string, query: string, options?: any): Promise<any[]>
Chat API (sdk.chat) - AI чат
// Streaming
streamChat(messages: ChatMessage[], options?: ChatStreamOptions): AsyncGenerator<ChatStreamChunk>

// Обычный
chat(messages: ChatMessage[], options?: ChatOptions): Promise<ChatResponse>
chatWithRegionFailover(messages: ChatMessage[], options?: ChatOptions): Promise<ChatResponse>

WebSocket API (sdk.projectSync)

// Подключение
await sdk.connectWebSocket()
sdk.disconnectWebSocket()
sdk.isWebSocketConnected

// Подписка
sdk.projectSync.subscribeToProject(projectId)
sdk.projectSync.unsubscribeFromProject(projectId)

// События
sdk.projectSync.on('sync-status-update', callback)  // {projectId, status, timestamp}
sdk.projectSync.on('sync-progress', callback)       // {projectId, progress, processedFiles, totalFiles}
sdk.projectSync.on('sync-completed', callback)      // {projectId, processedChunks, statistics}
sdk.projectSync.on('sync-error', callback)          // {projectId, error, timestamp}

// Пример: Cursor-like прогресс по файлам (v7.1.1+)
sdk.projectSync.on('sync-progress', (data) => {
  if (data.totalFiles && data.processedFiles) {
    // User-friendly: "450 of 722 files"
    console.log(`${data.processedFiles}/${data.totalFiles} files (${data.progress}%)`);
    updateUI(data.processedFiles, data.totalFiles, data.progress);
  } else {
    // Fallback: chunks (для старых версий backend)
    console.log(`${data.currentChunk}/${data.totalChunks} chunks`);
  }
});

sdk.projectSync.on('sync-completed', (data) => {
  const files = data.statistics?.totalFiles || 0;
  console.log(`Indexed ${files} files in ${data.duration}ms`);
});

🔧 Как работать с SDK

Индексация проекта

// ✅ ЕДИНСТВЕННЫЙ способ индексации (v6.2.2+):
await sdk.deltaManager.syncEncryptedChunks(projectId, chunks, rootHash, {
  batchSize: 50,
  onProgress: (current, total) => console.log(`${current}/${total}`)
});

// ❌ УДАЛЕНО: sdk.projects.startIndexing() - больше не существует
// ❌ УДАЛЕНО: filesystem индексация на сервере

Важно: Client Extension создает chunks → SDK отправляет на сервер → Сервер индексирует

Session Recovery

// Всегда проверять при старте проекта
const recovery = await sdk.projects.getRecoveryStatus(projectId);
if (recovery.needsRecovery) {
  // ✨ v6.2.2+: показывает "480 из 642 файлов" вместо "0 из 0"
  // Показать диалог: Continue (67%) — 480/642 files | Start Fresh
  await sdk.projects.resumeSync(projectId);  // или cancelRecovery()
}

WebSocket

  • Требуется OAuth токен в production
  • Автоматическое переподключение при разрыве
  • События фильтруются по projectId
  • Fallback на HTTP polling если недоступен

Авторизация

  • Локально: apiKey для HTTP API
  • Production: OAuth access_token для HTTP + WebSocket
  • v6.1.4+: getAuthToken() для автоматического обновления токенов

Graceful Auth Errors (v7.0.1+)

По умолчанию SDK тихо обрабатывает 401/403 ошибки (возвращает null) вместо выброса исключения. Это предотвращает ERR логи в VS Code Extension Host.

const sdk = await CodeSolverSDK.create({
  baseURL: 'https://workai.su/api/v1',
  suppressAuthErrors: true  // По умолчанию true
});

// Если токен истек, SDK вернет null вместо throw
const projects = await sdk.projects.getAllProjects();
if (!projects) {
  // Extension сам обрабатывает auth ошибку
  await authManager.refreshToken();
}

Отключить (старое поведение):

const sdk = await CodeSolverSDK.create({
  suppressAuthErrors: false  // SDK будет бросать ошибки как раньше
});

📝 Примеры

Базовый workflow

const sdk = await CodeSolverSDK.create({
  baseURL: 'https://workai.su/api/v1',
  getAuthToken: () => authManager.getAccessToken()
});

// 1. Найти/создать проект
const project = await sdk.projects.findOrCreateProject('/path/to/project');

// 2. Проверить recovery
const recovery = await sdk.projects.getRecoveryStatus(project.id);
if (recovery.needsRecovery) {
  await sdk.projects.resumeSync(project.id);
  return;
}

// 3. Проверить нужна ли синхронизация
const state = await sdk.projects.getProjectState(project.id, clientRootHash);
if (!state.syncRequired) {
  console.log('Проект синхронизирован');
  return;
}

// 4. Подключить WebSocket для прогресса
await sdk.connectWebSocket();
sdk.projectSync.subscribeToProject(project.id);
sdk.projectSync.on('sync-progress', (data) => {
  console.log(`Прогресс: ${data.progress}%`);
});

// 5. Синхронизировать
await sdk.deltaManager.syncEncryptedChunks(project.id, chunks, rootHash);

// 6. Поиск по проекту
const results = await sdk.search.searchCode(project.id, 'function handleClick');

Chat с streaming

for await (const chunk of sdk.chat.streamChat(messages, { model: 'claude-3-5-sonnet' })) {
  if (chunk.type === 'content_block_delta') {
    process.stdout.write(chunk.delta.text);
  }
}

ℹ️ Архитектура

Pure Cloud-First:

  • Backend SDK — HTTP клиент (НЕ работает с файлами)
  • Client Extension — сканирует файлы, создает chunks
  • Только Delta-Chunking — filesystem индексация удалена
Миграция с v6.x

BREAKING CHANGES:

  • ❌ Удален sdk.projects.startIndexing()
  • ✅ Используйте sdk.deltaManager.syncEncryptedChunks()
// ❌ Старый код:
// await sdk.projects.startIndexing(projectId);

// ✅ Новый код:
const chunks = await extension.scanAndCreateChunks();
const hash = extension.calculateMerkleRoot();
await sdk.deltaManager.syncEncryptedChunks(projectId, chunks, hash);

📦 Ключевые типы данных

ProjectState (v7.1.0+)

interface ProjectState {
  projectId: string;
  projectName: string;
  merkleRootHash: string | null;      // Server Merkle root для сравнения
  lastIndexedAt: Date | null;
  totalChunks: number;                // Технические детали
  totalFiles: number;                 // ✨ Количество файлов для UI
  indexingStatus: 'pending' | 'in-progress' | 'complete' | 'failed' | 'cancelled';
  syncRequired?: boolean;             // true если нужна синхронизация
  lastSyncSessionId?: string;
}

// Использование:
const state = await sdk.projects.getProjectState(projectId, clientRootHash);
if (state.syncRequired) {
  // Нужна синхронизация
}
console.log(`${state.totalFiles} files indexed`);

SyncProgressEvent (v7.1.1+)

interface SyncProgressEvent {
  projectId: string;
  sessionId: string;
  stage: 'receiving_chunks' | 'processing_chunks' | 'creating_embeddings' | 'updating_tree' | 'finalizing';
  progress: number;                   // 0-100
  
  // User-friendly (файлы) - основное для UI
  processedFiles?: number;            // 450
  totalFiles?: number;                // 722
  
  // Technical (чанки) - опционально
  currentChunk?: number;
  totalChunks?: number;
  
  estimatedTimeRemaining?: number;    // секунды
  details?: string;
  timestamp: Date;
}

// Использование:
sdk.projectSync.on('sync-progress', (data) => {
  if (data.totalFiles) {
    statusBar.text = `${data.processedFiles}/${data.totalFiles} files`;
  }
});

SyncCompletedEvent (v7.1.1+)

interface SyncCompletedEvent {
  projectId: string;
  sessionId: string;
  status: 'success' | 'failed' | 'cancelled';
  totalProcessed: number;
  duration: number;                   // миллисекунды
  errors?: string[];
  warnings?: string[];
  statistics?: {
    filesIndexed: number;
    embeddingsCreated: number;
    chunksProcessed: number;
    totalSize: number;
    totalFiles?: number;              // ✨ Для финального отображения
  };
}

RecoveryInfo

interface RecoveryInfo {
  sessionId: string;
  projectId: string;
  projectName: string;
  status: 'interrupted' | 'paused' | 'active' | 'uploading';
  progress: {
    received: number;                 // Чанки получены
    processed: number;                // Чанки обработаны
    total: number;                    // Всего ожидается
    percentage: number;               // 0-100
    processedFiles?: number;          // ✨ Файлы обработаны
    totalFiles?: number;              // ✨ Всего файлов
    filesPercentage?: number;         // ✨ 0-100 по файлам
  };
  canResume: boolean;
  needsRecovery: boolean;
  interruptedAt?: Date;
}

🆘 Быстрое решение проблем

Проблема: "0 files" после индексации

// Проверить:
const state = await sdk.projects.getProjectState(projectId);
console.log(state.totalFiles); // Должно быть > 0

// Если 0 - backend не сохранил totalFiles
// Решение: Обновите backend до последней версии

Проблема: Нет WebSocket обновлений

// 1. Проверить подключение
console.log(sdk.isWebSocketConnected); // должно быть true

// 2. Проверить подписку
sdk.projectSync.subscribeToProject(projectId);

// 3. Проверить токен (production)
const sdk = await CodeSolverSDK.create({
  baseURL: 'https://workai.su/api/v1',
  getAuthToken: () => authManager.getAccessToken() // OAuth required
});

// 4. Fallback на HTTP polling
const status = await sdk.projects.getIndexingStatus(projectId);

Проблема: Прерванная синхронизация

// Проверить recovery status
const recovery = await sdk.projects.getRecoveryStatus(projectId);
if (recovery.needsRecovery) {
  // Показать UI диалог
  const percentage = recovery.progress.filesPercentage || recovery.progress.percentage;
  const files = `${recovery.progress.processedFiles}/${recovery.progress.totalFiles}`;
  
  // Пользователь выбирает:
  await sdk.projects.resumeSync(projectId);      // Продолжить
  // или
  await sdk.projects.cancelRecovery(projectId);  // Начать заново
}

Проблема: 401/403 ошибки

// v7.0.1+: По умолчанию возвращает null
const projects = await sdk.projects.getAllProjects();
if (!projects) {
  // Токен истек
  await authManager.refreshToken();
  // Повторить запрос
}

// Старое поведение (throw):
const sdk = await CodeSolverSDK.create({
  suppressAuthErrors: false
});

📚 Справка

TypeScript типы

import { 
  ChatMessage, 
  ChatOptions, 
  Project, 
  ProjectState,
  SyncProgressEvent,
  SyncCompletedEvent,
  RecoveryInfo
} from '@androidsprintteam/solver-sdk';

Debug режим

const sdk = await CodeSolverSDK.create({
  debug: 'verbose',  // silent | error | warn | info | debug | verbose
  webSocket: { debug: true }
});

Обработка ошибок

try {
  await sdk.projects.getProject(projectId);
} catch (error) {
  console.error(`[HTTP ${error.status}] ${error.message}`);
}

📄 License

MIT