Dropstone CLI

SDK do Dropstone

Cliente JS com tipagem segura para o runtime do agente Dropstone. As sessões do SDK herdam a memória entre superfícies do Dropstone (Continuity) — uma memória persistente compartilhada com o CLI, chat e SDK.

O SDK JS/TS do Dropstone fornece um cliente com tipagem segura para interagir com o runtime do agente Dropstone. Ele inicia dropstone serve como um subprocesso e fornece um cliente HTTP tipado apontando para ele. Como executa o mesmo agente local que o CLI usa, cada sessão do SDK herda a memória da sua conta — ensine uma vez no CLI, chat ou SDK e todas as superfícies já sabem (veja Memória (Continuity)).

Precisa de acesso headless à API em CI?

Para uso puramente programático (pipelines de CI, automação, serverless), prefira a API HTTP com uma DROPSTONE_API_KEY. O SDK desta página é destinado a incorporar o agente interativo em um processo Node onde o binário do CLI está instalado junto.

Veja a página Servidor para entender como a API HTTP subjacente funciona.


Instalação

Instale o SDK a partir do npm:

npm install @blankline/dropstone-sdk

Cliente headless de API

Para pipelines de CI, automação e serverless, você não precisa do CLI. Use o cliente headless, que fala diretamente com a API HTTP do Dropstone usando uma chave de API:

import { createDropstoneApi } from "@blankline/dropstone-sdk"

// Lê DROPSTONE_API_KEY do ambiente automaticamente.
const dropstone = createDropstoneApi()

const resp = await dropstone.chat.completions.create({
  model: "dropstone-fast", // ou "dropstone-pro" / "dropstone-heavy"
  messages: [{ role: "user", content: "Escreva um haiku sobre depuração." }],
})

console.log(resp.choices[0].message.content)
console.log("Custo: $" + resp.usage?.cost) // valor cobrado, em USD

Obter uma chave de API

  1. Entre em dropstone.io/dashboard.
  2. Abra Configurações → API em dropstone.io/dashboard/settings e crie uma chave — ela se parece com dsk_live_<43 caracteres>.
  3. Defina-a como uma variável de ambiente (ou passe apiKey para createDropstoneApi):
export DROPSTONE_API_KEY=dsk_live_...

Note

Trate sua chave de API como uma senha. Nunca a envie para o git nem a incorpore em um bundle de frontend. Requisições com chave de API são cobradas por uso a partir do seu saldo de crédito pré-pago — as franquias do plano não se aplicam. Veja Uso e limites.

O streaming funciona da mesma forma, e o cliente é compatível com OpenAI — você pode apontar o SDK da OpenAI para a URL base do Dropstone:

const stream = await dropstone.chat.completions.create({
  model: "dropstone-fast",
  stream: true,
  messages: [{ role: "user", content: "Conte até 5." }],
})

for await (const chunk of stream) {
  process.stdout.write(chunk.choices?.[0]?.delta?.content ?? "")
}

Veja a página API HTTP para a referência completa.


Criar cliente

Crie uma instância do dropstone:

import { createDropstone } from "@blankline/dropstone-sdk"

const { client } = await createDropstone()

Isso inicia tanto um servidor quanto um cliente. Cada método do SDK retorna { data, request, response }, então acesse o payload via .data.

Por padrão, o servidor entra como quem executou dropstone auth login naquela máquina. Em um host sem supervisão não existe tal conta, então passe uma chave de API e uma lista de permissões explícita através de 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" },
  },
})

Ambas as partes são necessárias. Chaves de API são rejeitadas em /v1, e o agente build padrão pergunta antes de cada chamada de ferramenta, então sem uma lista de permissões a execução fica esperando uma aprovação que ninguém pode dar e trava em vez de falhar. Enviar "agent": "accept all" no prompt é a alternativa à lista de permissões. Veja Servidor e Permissões.

Opções

OpçãoTipoDescriçãoPadrão
hostnamestringHostname do servidor127.0.0.1
portnumberPorta do servidor4096
signalAbortSignalSinal de abortamentoundefined
timeoutnumberTimeout em ms para iniciar5000
configConfigObjeto de configuração{}

Config

Você pode passar um objeto de configuração para personalizar o comportamento. A instância ainda carrega seu dropstone.json, mas você pode sobrescrever ou adicionar configuração inline:

import { createDropstone } from "@blankline/dropstone-sdk"

const dropstone = await createDropstone({
  hostname: "127.0.0.1",
  port: 4096,
  config: {
    model: "dropstone/dropstone-pro",
  },
})

console.log(`Servidor rodando em ${dropstone.server.url}`)

dropstone.server.close()

Memória (Continuity)

O Dropstone mantém uma memória persistente por conta. Chamamos isso de Continuity — uma memória entre superfícies compartilhada pelo CLI, chat, VS Code e SDK. Ensine uma vez em qualquer lugar e todas as superfícies já sabem.

As sessões que você executa através do SDK usam a mesma memória de conta que o CLI. O que você ensina ao CLI já é conhecido pelas sessões do SDK, e o que uma sessão do SDK registra está disponível de volta no CLI e no chat. A cada turno, o agente recupera automaticamente a memória relevante antes de responder, então ele não reaprende o que já sabe.

O agente do SDK também pode ler e escrever memória diretamente, com as mesmas ferramentas do CLI:

FerramentaFinalidade
memory_recallRecupera as lições mais relevantes para a tarefa atual
record_lessonSalva uma lição durável (uma regra ou um fato)
list_lessonsMostra tudo o que ele aprendeu sobre você
forget_lessonRemove uma lição

Note

A memória exige que você esteja conectado ao Dropstone. O servidor do SDK lê o mesmo auth.json que o CLI, então entre uma vez com dropstone e as sessões do SDK herdam a mesma memória de conta. Nada é armazenado se você não estiver conectado.

Exemplo

Declare uma preferência a partir do código e ela será registrada da mesma forma que seria no CLI — visível no CLI e no chat depois:

const { client } = await createDropstone()

const session = await client.session.create({ body: { title: "Ensinar memória" } })

await client.session.prompt({
  path: { id: session.data.id },
  body: {
    parts: [{ type: "text", text: "Lembre disso como uma regra permanente: use sempre bun, não npm." }],
  },
})

Continuity vs AGENTS.md

AGENTS.md é um arquivo de projeto que você envia para o Git para convenções de equipe — estável, com escopo de repositório e compartilhado com qualquer pessoa que o clone. Continuity é sua memória privada de conta: o que você ensina no CLI, chat ou SDK segue sua conta entre superfícies e projetos. Eles resolvem problemas diferentes e funcionam melhor juntos — regras de projeto em AGENTS.md, memória pessoal entre superfícies em Continuity. Veja Regras e Memória.

O que é lembrado

A memória armazena apenas as regras e fatos que você ensina explicitamente — preferências, convenções e correções que valem a pena carregar entre sessões. Ela é distinta do contexto efêmero de uma única sessão, que não é mantido após o fim da sessão.

Veja a página Memória para saber como o Dropstone decide o que manter.


Somente cliente

Se você já tem uma instância do dropstone em execução, pode criar uma instância de cliente para se conectar a ela:

import { createDropstoneClient } from "@blankline/dropstone-sdk"

const client = createDropstoneClient({
  baseUrl: "http://localhost:4096",
})

Opções

OpçãoTipoDescriçãoPadrão
baseUrlstringURL do servidorhttp://localhost:4096
fetchfunctionImplementação customizada de fetchglobalThis.fetch
parseAsstringMétodo de análise da respostaauto
responseStylestringEstilo de retorno: data ou fieldsfields
throwOnErrorbooleanLançar erros em vez de retornarfalse

Tipos

O SDK inclui definições TypeScript para todos os tipos da API. Importe-os diretamente:

import type { Session, Message, Part } from "@blankline/dropstone-sdk"

Todos os tipos são gerados a partir da especificação OpenAPI do servidor, então os nomes que você vê em TypeScript mapeiam um a um para as formas de requisição e resposta do servidor.


Erros

O SDK pode lançar erros que você pode capturar e tratar:

try {
  await client.session.get({ path: { id: "invalid-id" } })
} catch (error) {
  console.error("Falha ao obter sessão:", (error as Error).message)
}

Saída Estruturada

Você pode solicitar saída JSON estruturada do modelo especificando um format com um esquema JSON. O modelo usará uma ferramenta StructuredOutput para retornar JSON validado que corresponde ao seu esquema.

Uso Básico

const result = await client.session.prompt({
  path: { id: sessionId },
  body: {
    parts: [{ type: "text", text: "Pesquise o Dropstone e forneça informações da empresa" }],
    format: {
      type: "json_schema",
      schema: {
        type: "object",
        properties: {
          company: { type: "string", description: "Nome da empresa" },
          founded: { type: "number", description: "Ano de fundação" },
          products: {
            type: "array",
            items: { type: "string" },
            description: "Principais produtos",
          },
        },
        required: ["company", "founded"],
      },
    },
  },
})

// Acesse a saída estruturada
console.log(result.data.info.structured_output)
// { company: "Dropstone", founded: 2024, products: ["Dropstone CLI"] }

Tipos de Formato de Saída

TipoDescrição
textPadrão. Resposta de texto padrão (sem saída estruturada)
json_schemaRetorna JSON validado que corresponde ao esquema fornecido

Formato de Esquema JSON

Ao usar type: 'json_schema', forneça:

CampoTipoDescrição
type'json_schema'Obrigatório. Especifica o modo de esquema JSON
schemaobjectObrigatório. Objeto de esquema JSON que define a estrutura de saída
retryCountnumberOpcional. Número de tentativas de validação (padrão: 2)

Tratamento de Erros

Se o modelo falhar em produzir saída estruturada válida após todas as tentativas, a resposta incluirá um StructuredOutputError:

if (result.data.info.error?.name === "StructuredOutputError") {
  console.error("Falha ao produzir saída estruturada:", result.data.info.error.message)
  console.error("Tentativas:", result.data.info.error.retries)
}

Melhores Práticas

  1. Forneça descrições claras nas propriedades do seu esquema para ajudar o modelo a entender quais dados extrair
  2. Use required para especificar quais campos devem estar presentes
  3. Mantenha esquemas focados - esquemas aninhados complexos podem ser mais difíceis para o modelo preencher corretamente
  4. Defina retryCount apropriado - aumente para esquemas complexos, diminua para simples

APIs

O SDK expõe todas as APIs do servidor através de um cliente com tipagem segura.


Global

MétodoDescriçãoResposta
global.health()Verifica saúde e versão do servidor{ healthy: true, version: string }

Exemplos

const health = await client.global.health()
console.log(health.data.version)

App

MétodoDescriçãoResposta
app.log()Escreve uma entrada de logboolean
app.agents()Lista todos os agentes disponíveisAgent[]

Exemplos

// Escreve uma entrada de log
await client.app.log({
  body: {
    service: "my-app",
    level: "info",
    message: "Operação concluída",
  },
})

// Lista agentes disponíveis
const agents = await client.app.agents()

Projeto

MétodoDescriçãoResposta
project.list()Lista todos os projetosProject[]
project.current()Obtém o projeto atualProject

Exemplos

// Lista todos os projetos
const projects = await client.project.list()

// Obtém o projeto atual
const currentProject = await client.project.current()

Caminho

MétodoDescriçãoResposta
path.get()Obtém o caminho atualPath

Exemplos

// Obtém informações do caminho atual
const pathInfo = await client.path.get()

Config

MétodoDescriçãoResposta
config.get()Obtém informações de configConfig

Exemplos

const config = await client.config.get()

Sessões

MétodoDescriçãoNotas
session.list()Lista sessõesRetorna Session[]
session.get({ path })Obtém sessãoRetorna Session
session.children({ path })Lista sessões filhasRetorna Session[]
session.create({ body })Cria sessãoRetorna Session
session.delete({ path })Exclui sessãoRetorna boolean
session.update({ path, body })Atualiza propriedades da sessãoRetorna Session
session.init({ path, body })Analisa o app e cria AGENTS.mdRetorna boolean
session.abort({ path })Aborta uma sessão em execuçãoRetorna boolean
session.summarize({ path, body })Resume a sessãoRetorna boolean
session.messages({ path })Lista mensagens em uma sessãoRetorna { info: Message, parts: Part[]}[]
session.message({ path })Obtém detalhes da mensagemRetorna { info: Message, parts: Part[]}
session.prompt({ path, body })Envia mensagem de promptbody.noReply: true retorna UserMessage (somente contexto). Padrão retorna AssistantMessage com resposta da IA. Suporta body.outputFormat para saída estruturada
session.command({ path, body })Envia comando para a sessãoRetorna { info: AssistantMessage, parts: Part[]}
session.shell({ path, body })Executa um comando de shellRetorna AssistantMessage
session.revert({ path, body })Reverte uma mensagemRetorna Session
session.unrevert({ path })Restaura mensagens revertidasRetorna Session
postSessionByIdPermissionsByPermissionId({ path, body })Responde a uma solicitação de permissãoRetorna boolean

Exemplos

// Cria e gerencia sessões
const session = await client.session.create({
  body: { title: "Minha sessão" },
})

const sessions = await client.session.list()

// Envia uma mensagem de prompt
const result = await client.session.prompt({
  path: { id: session.data.id },
  body: {
    model: { providerID: "dropstone", modelID: "dropstone-pro" },
    parts: [{ type: "text", text: "Olá!" }],
  },
})

// Injeta contexto sem acionar resposta da IA (útil para plugins)
await client.session.prompt({
  path: { id: session.data.id },
  body: {
    noReply: true,
    parts: [{ type: "text", text: "Você é um assistente útil." }],
  },
})

Arquivos

MétodoDescriçãoResposta
find.text({ query })Busca texto em arquivosArray de objetos de correspondência com path, lines, line_number, absolute_offset, submatches
find.files({ query })Encontra arquivos e diretórios por nomestring[] (caminhos)
find.symbols({ query })Encontra símbolos do workspaceSymbol[]
file.read({ query })Lê um arquivo{ type: "raw" | "patch", content: string }
file.status({ query? })Obtém status de arquivos rastreadosFile[]

find.files suporta alguns campos de consulta opcionais:

  • type: "file" ou "directory"
  • directory: sobrescreve a raiz do projeto para a busca
  • limit: máximo de resultados (1–200)

Exemplos

// Busca e lê arquivos
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

MétodoDescriçãoResposta
auth.set({ ... })Define credenciais de autenticaçãoboolean

Exemplos

await client.auth.set({
  path: { id: "dropstone" },
  body: { type: "api", key: "sua-chave-de-api-dropstone" },
})

Eventos

MétodoDescriçãoResposta
event.subscribe()Stream de eventos enviados pelo servidorStream de eventos enviados pelo servidor

Exemplos

// Escuta eventos em tempo real
const events = await client.event.subscribe()
for await (const event of events.stream) {
  console.log("Evento:", event.type, event.properties)
}

API v2

O SDK inclui uma superfície v1 estável (usada nos exemplos acima) e uma superfície v2 que espelha o contrato mais recente do Effect HttpApi. Prefira v2 para novas integrações:

import { createDropstone } from "@blankline/dropstone-sdk/v2"

const { client, server } = await createDropstone()
// v2 expõe uma hierarquia de recursos mais rica: workspace, worktree, file, find, etc.
const files = await client.file.list({ path: "src" })

A superfície v1 é mantida para compatibilidade retroativa. Novos endpoints só entram na v2.

Ctrl+I