Dropstone SDK
Типобезопасный JS-клиент для агентной среды выполнения Dropstone. Сессии SDK наследуют кросс-поверхностную память Dropstone (Continuity) — одну постоянную память, общую с CLI, чатом и SDK.
SDK Dropstone для JS/TS предоставляет типобезопасный клиент для взаимодействия с агентной средой выполнения Dropstone. Он запускает dropstone serve как подпроцесс и предоставляет типизированный HTTP-клиент, указывающий на него. Поскольку он использует того же локального агента, что и CLI, каждая сессия SDK наследует память вашей учетной записи — обучите его один раз в CLI, чате или SDK, и каждая поверхность уже будет это знать (см. Память (Continuity)).
Нужен headless-доступ к API в CI?:
Для чисто программного использования (CI-пайплайны, автоматизация, serverless) предпочтительнее использовать HTTP API с DROPSTONE_API_KEY. SDK на этой странице предназначен для встраивания интерактивного агента в процесс Node, где рядом установлен бинарный файл CLI.
См. страницу Сервер для получения информации о том, как работает базовый HTTP API.
Установка
Установите SDK из npm:
npm install @blankline/dropstone-sdk
Headless-клиент API
Для CI-пайплайнов, автоматизации и serverless CLI вообще не нужен. Используйте headless-клиент, который напрямую общается с HTTP API Dropstone с помощью API-ключа:
import { createDropstoneApi } from "@blankline/dropstone-sdk"
// Автоматически считывает DROPSTONE_API_KEY из окружения.
const dropstone = createDropstoneApi()
const resp = await dropstone.chat.completions.create({
model: "dropstone-fast", // или "dropstone-pro" / "dropstone-heavy"
messages: [{ role: "user", content: "Напиши хайку об отладке." }],
})
console.log(resp.choices[0].message.content)
console.log("Стоимость: $" + resp.usage?.cost) // списанная сумма, в долларах США
Получение API-ключа
- Войдите на dropstone.io/dashboard.
- Откройте Настройки → API на dropstone.io/dashboard/settings и создайте ключ — он выглядит как
dsk_live_<43 символа>. - Установите его как переменную окружения (или передайте
apiKeyвcreateDropstoneApi):
export DROPSTONE_API_KEY=dsk_live_...
Note:
Относитесь к своему API-ключу как к паролю. Никогда не коммитьте его в git и не встраивайте во фронтенд-бандл. Запросы с API-ключом тарифицируются по мере использования с вашего предоплаченного баланса — лимиты тарифных планов не применяются. См. Использование и лимиты.
Стриминг работает так же, и клиент совместим с OpenAI — вы можете указать базовый URL Dropstone в OpenAI SDK:
const stream = await dropstone.chat.completions.create({
model: "dropstone-fast",
stream: true,
messages: [{ role: "user", content: "Посчитай до 5." }],
})
for await (const chunk of stream) {
process.stdout.write(chunk.choices?.[0]?.delta?.content ?? "")
}
Полную справочную информацию см. на странице HTTP API.
Создание клиента
Создайте экземпляр dropstone:
import { createDropstone } from "@blankline/dropstone-sdk"
const { client } = await createDropstone()
Это запускает и сервер, и клиент. Каждый метод SDK возвращает { data, request, response }, поэтому доступ к полезной нагрузке осуществляется через .data.
По умолчанию сервер входит в систему как тот, кто выполнял dropstone auth login на этой машине. На необслуживаемом хосте такой учетной записи нет, поэтому передайте API-ключ и явный список разрешений через config:
const { client } = await createDropstone({
config: {
provider: {
dropstone: {
options: {
apiKey: process.env.DROPSTONE_API_KEY,
baseURL: "https://api.dropstone.io/api/v1",
},
},
},
permission: { "*": "deny", read: "allow", edit: "allow", glob: "allow", grep: "allow", bash: "allow" },
},
})
Обе части обязательны. API-ключи отклоняются на /v1, а агент build по умолчанию спрашивает разрешение перед каждым вызовом инструмента, поэтому без списка разрешений запуск будет ждать одобрения, которое никто не может дать, и зависнет, а не завершится ошибкой. Отправка "agent": "accept all" в промпте — альтернатива списку разрешений. См. Сервер и Разрешения.
Параметры
| Параметр | Тип | Описание | По умолчанию |
|---|---|---|---|
hostname | string | Имя хоста сервера | 127.0.0.1 |
port | number | Порт сервера | 4096 |
signal | AbortSignal | Сигнал отмены для прерывания | undefined |
timeout | number | Таймаут в мс для запуска сервера | 5000 |
config | Config | Объект конфигурации | {} |
Конфигурация
Вы можете передать объект конфигурации для настройки поведения. Экземпляр по-прежнему подхватывает ваш dropstone.json, но вы можете переопределить или дополнить конфигурацию встроенно:
import { createDropstone } from "@blankline/dropstone-sdk"
const dropstone = await createDropstone({
hostname: "127.0.0.1",
port: 4096,
config: {
model: "dropstone/dropstone-pro",
},
})
console.log(`Сервер запущен на ${dropstone.server.url}`)
dropstone.server.close()
Память (Continuity)
Dropstone хранит одну постоянную память на учетную запись. Мы называем это Continuity — кросс-поверхностная память, общая для CLI, чата, VS Code и SDK. Обучите его один раз где угодно, и каждая поверхность уже будет это знать.
Сессии, которые вы запускаете через SDK, используют ту же память учетной записи, что и CLI. То, чему вы научили CLI, уже известно сессиям SDK, а то, что записала сессия SDK, доступно обратно в CLI и чате. На каждом ходу агент автоматически вспоминает релевантную память перед ответом, поэтому ему не нужно заново учить то, что он уже знает.
Агент SDK также может напрямую читать и записывать память, используя те же инструменты, что и CLI:
| Инструмент | Назначение |
|---|---|
memory_recall | Вспомнить наиболее релевантные уроки для текущей задачи |
record_lesson | Сохранить долговременный урок (правило или факт) |
list_lessons | Показать все, что он узнал о вас |
forget_lesson | Удалить урок |
Note:
Память требует входа в Dropstone. Сервер SDK читает тот же auth.json, что и CLI, поэтому войдите один раз с помощью dropstone, и сессии SDK унаследуют ту же память учетной записи. Если вы не вошли, ничего не сохраняется.
Пример
Укажите предпочтение из кода, и оно будет записано так же, как в CLI — видимо в CLI и чате впоследствии:
const { client } = await createDropstone()
const session = await client.session.create({ body: { title: "Обучить память" } })
await client.session.prompt({
path: { id: session.data.id },
body: {
parts: [{ type: "text", text: "Запомни это как постоянное правило: всегда используй bun, а не npm." }],
},
})
Continuity против AGENTS.md
AGENTS.md — это проектный файл, который вы коммитите в Git для командных соглашений — стабильный, ограниченный репозиторием и доступный всем, кто его клонирует. Continuity — это ваша личная память учетной записи: то, чему вы учите в CLI, чате или SDK, следует за вашей учетной записью по поверхностям и проектам. Они решают разные задачи и лучше всего работают вместе — правила проекта в AGENTS.md, личная кросс-поверхностная память в Continuity. См. Правила и Память.
Что запоминается
Память хранит только те правила и факты, которым вы явно ее обучаете — предпочтения, соглашения и исправления, которые стоит переносить между сессиями. Она отличается от эфемерного контекста отдельной сессии, который не сохраняется после ее завершения.
См. страницу Память для получения информации о том, как Dropstone решает, что сохранять.
Только клиент
Если у вас уже есть запущенный экземпляр dropstone, вы можете создать клиентский экземпляр для подключения к нему:
import { createDropstoneClient } from "@blankline/dropstone-sdk"
const client = createDropstoneClient({
baseUrl: "http://localhost:4096",
})
Параметры
| Параметр | Тип | Описание | По умолчанию |
|---|---|---|---|
baseUrl | string | URL сервера | http://localhost:4096 |
fetch | function | Пользовательская реализация fetch | globalThis.fetch |
parseAs | string | Метод разбора ответа | auto |
responseStyle | string | Стиль возврата: data или fields | fields |
throwOnError | boolean | Выбрасывать ошибки вместо возврата | false |
Типы
SDK включает определения TypeScript для всех типов API. Импортируйте их напрямую:
import type { Session, Message, Part } from "@blankline/dropstone-sdk"
Все типы генерируются из спецификации OpenAPI сервера, поэтому имена, которые вы видите в TypeScript, сопоставляются один к одному с формами запросов и ответов сервера.
Ошибки
SDK может выбрасывать ошибки, которые вы можете перехватывать и обрабатывать:
try {
await client.session.get({ path: { id: "invalid-id" } })
} catch (error) {
console.error("Не удалось получить сессию:", (error as Error).message)
}
Структурированный вывод
Вы можете запросить структурированный JSON-вывод от модели, указав format с JSON-схемой. Модель будет использовать инструмент StructuredOutput для возврата проверенного JSON, соответствующего вашей схеме.
Базовое использование
const result = await client.session.prompt({
path: { id: sessionId },
body: {
parts: [{ type: "text", text: "Исследуй Dropstone и предоставь информацию о компании" }],
format: {
type: "json_schema",
schema: {
type: "object",
properties: {
company: { type: "string", description: "Название компании" },
founded: { type: "number", description: "Год основания" },
products: {
type: "array",
items: { type: "string" },
description: "Основные продукты",
},
},
required: ["company", "founded"],
},
},
},
})
// Доступ к структурированному выводу
console.log(result.data.info.structured_output)
// { company: "Dropstone", founded: 2024, products: ["Dropstone CLI"] }
Типы форматов вывода
| Тип | Описание |
|---|---|
text | По умолчанию. Стандартный текстовый ответ (без структурированного вывода) |
json_schema | Возвращает проверенный JSON, соответствующий предоставленной схеме |
Формат JSON-схемы
При использовании type: 'json_schema' укажите:
| Поле | Тип | Описание |
|---|---|---|
type | 'json_schema' | Обязательно. Указывает режим JSON-схемы |
schema | object | Обязательно. Объект JSON-схемы, определяющий структуру вывода |
retryCount | number | Необязательно. Количество попыток проверки (по умолчанию: 2) |
Обработка ошибок
Если модели не удается создать корректный структурированный вывод после всех попыток, ответ будет содержать StructuredOutputError:
if (result.data.info.error?.name === "StructuredOutputError") {
console.error("Не удалось создать структурированный вывод:", result.data.info.error.message)
console.error("Попытки:", result.data.info.error.retries)
}
Рекомендации
- Предоставляйте четкие описания в свойствах схемы, чтобы помочь модели понять, какие данные извлекать
- Используйте
required, чтобы указать, какие поля должны присутствовать - Держите схемы сфокусированными — сложные вложенные схемы могут быть труднее для корректного заполнения моделью
- Устанавливайте подходящий
retryCount— увеличьте для сложных схем, уменьшите для простых
API
SDK предоставляет все серверные API через типобезопасный клиент.
Global
| Метод | Описание | Ответ |
|---|---|---|
global.health() | Проверить здоровье сервера и версию | { healthy: true, version: string } |
Примеры
const health = await client.global.health()
console.log(health.data.version)
App
| Метод | Описание | Ответ |
|---|---|---|
app.log() | Записать запись в журнал | boolean |
app.agents() | Список всех доступных агентов | Agent[] |
Примеры
// Записать запись в журнал
await client.app.log({
body: {
service: "my-app",
level: "info",
message: "Операция завершена",
},
})
// Список доступных агентов
const agents = await client.app.agents()
Project
| Метод | Описание | Ответ |
|---|---|---|
project.list() | Список всех проектов | Project[] |
project.current() | Получить текущий проект | Project |
Примеры
// Список всех проектов
const projects = await client.project.list()
// Получить текущий проект
const currentProject = await client.project.current()
Path
| Метод | Описание | Ответ |
|---|---|---|
path.get() | Получить текущий путь | Path |
Примеры
// Получить информацию о текущем пути
const pathInfo = await client.path.get()
Config
| Метод | Описание | Ответ |
|---|---|---|
config.get() | Получить информацию о конфигурации | Config |
Примеры
const config = await client.config.get()
Sessions
| Метод | Описание | Примечания |
|---|---|---|
session.list() | Список сессий | Возвращает Session[] |
session.get({ path }) | Получить сессию | Возвращает Session |
session.children({ path }) | Список дочерних сессий | Возвращает Session[] |
session.create({ body }) | Создать сессию | Возвращает Session |
session.delete({ path }) | Удалить сессию | Возвращает boolean |
session.update({ path, body }) | Обновить свойства сессии | Возвращает Session |
session.init({ path, body }) | Проанализировать приложение и создать AGENTS.md | Возвращает boolean |
session.abort({ path }) | Прервать запущенную сессию | Возвращает boolean |
session.summarize({ path, body }) | Суммировать сессию | Возвращает boolean |
session.messages({ path }) | Список сообщений в сессии | Возвращает { info: Message, parts: Part[]}[] |
session.message({ path }) | Получить детали сообщения | Возвращает { info: Message, parts: Part[]} |
session.prompt({ path, body }) | Отправить промпт-сообщение | body.noReply: true возвращает UserMessage (только контекст). По умолчанию возвращает AssistantMessage с ответом ИИ. Поддерживает body.outputFormat для структурированного вывода |
session.command({ path, body }) | Отправить команду в сессию | Возвращает { info: AssistantMessage, parts: Part[]} |
session.shell({ path, body }) | Выполнить команду оболочки | Возвращает AssistantMessage |
session.revert({ path, body }) | Откатить сообщение | Возвращает Session |
session.unrevert({ path }) | Восстановить откаченные сообщения | Возвращает Session |
postSessionByIdPermissionsByPermissionId({ path, body }) | Ответить на запрос разрешения | Возвращает boolean |
Примеры
// Создание и управление сессиями
const session = await client.session.create({
body: { title: "Моя сессия" },
})
const sessions = await client.session.list()
// Отправка промпт-сообщения
const result = await client.session.prompt({
path: { id: session.data.id },
body: {
model: { providerID: "dropstone", modelID: "dropstone-pro" },
parts: [{ type: "text", text: "Привет!" }],
},
})
// Внедрение контекста без запуска ответа ИИ (полезно для плагинов)
await client.session.prompt({
path: { id: session.data.id },
body: {
noReply: true,
parts: [{ type: "text", text: "Ты полезный ассистент." }],
},
})
Files
| Метод | Описание | Ответ |
|---|---|---|
find.text({ query }) | Поиск текста в файлах | Массив объектов совпадений с path, lines, line_number, absolute_offset, submatches |
find.files({ query }) | Поиск файлов и каталогов по имени | string[] (пути) |
find.symbols({ query }) | Поиск символов рабочей области | Symbol[] |
file.read({ query }) | Чтение файла | { type: "raw" | "patch", content: string } |
file.status({ query? }) | Получить статус отслеживаемых файлов | File[] |
find.files поддерживает несколько необязательных полей запроса:
type:"file"или"directory"directory: переопределить корень проекта для поискаlimit: максимальное количество результатов (1–200)
Примеры
// Поиск и чтение файлов
const textResults = await client.find.text({
query: { pattern: "function.*dropstone" },
})
const files = await client.find.files({
query: { query: "*.ts", type: "file" },
})
const directories = await client.find.files({
query: { query: "packages", type: "directory", limit: 20 },
})
const content = await client.file.read({
query: { path: "src/index.ts" },
})
Auth
| Метод | Описание | Ответ |
|---|---|---|
auth.set({ ... }) | Установить учетные данные | boolean |
Примеры
await client.auth.set({
path: { id: "dropstone" },
body: { type: "api", key: "your-dropstone-api-key" },
})
Events
| Метод | Описание | Ответ |
|---|---|---|
event.subscribe() | Поток событий, отправляемых сервером | Поток событий, отправляемых сервером |
Примеры
// Прослушивание событий в реальном времени
const events = await client.event.subscribe()
for await (const event of events.stream) {
console.log("Событие:", event.type, event.properties)
}
v2 API
SDK поставляется со стабильной поверхностью v1 (используется в примерах выше) и поверхностью v2, которая отражает более новый контракт Effect HttpApi. Для новых интеграций предпочтительнее v2:
import { createDropstone } from "@blankline/dropstone-sdk/v2"
const { client, server } = await createDropstone()
// v2 предоставляет более богатую иерархию ресурсов: workspace, worktree, file, find и т.д.
const files = await client.file.list({ path: "src" })
Поверхность v1 сохраняется для обратной совместимости. Новые конечные точки появляются только в v2.