Dropstone CLI

SDK Dropstone

Client JS typé pour le runtime d'agent Dropstone. Les sessions SDK héritent de la mémoire transversale de Dropstone (Continuity) — une mémoire persistante partagée avec le CLI, le chat et le SDK.

Le SDK JS/TS de Dropstone fournit un client typé pour interagir avec le runtime d'agent Dropstone. Il lance dropstone serve comme sous-processus et vous donne un client HTTP typé pointant vers celui-ci. Comme il exécute le même agent local que le CLI, chaque session SDK hérite de la mémoire de votre compte — enseignez une fois dans le CLI, le chat ou le SDK et chaque surface la connaît déjà (voir Mémoire (Continuity)).

Besoin d'un accès API headless dans le CI ?

Pour une utilisation purement programmatique (pipelines CI, automatisation, serverless), préférez l'API HTTP avec une DROPSTONE_API_KEY. Le SDK de cette page est destiné à intégrer l'agent interactif dans un processus Node où le binaire CLI est installé à côté.

Consultez la page Serveur pour comprendre comment fonctionne l'API HTTP sous-jacente.


Installation

Installez le SDK depuis npm :

npm install @blankline/dropstone-sdk

Client API headless

Pour les pipelines CI, l'automatisation et le serverless, vous n'avez pas besoin du CLI. Utilisez le client headless, qui communique directement avec l'API HTTP de Dropstone avec une clé API :

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

// Lit DROPSTONE_API_KEY depuis l'environnement automatiquement.
const dropstone = createDropstoneApi()

const resp = await dropstone.chat.completions.create({
  model: "dropstone-fast", // ou "dropstone-pro" / "dropstone-heavy"
  messages: [{ role: "user", content: "Écris un haïku sur le débogage." }],
})

console.log(resp.choices[0].message.content)
console.log("Coût : $" + resp.usage?.cost) // montant facturé, en USD

Obtenir une clé API

  1. Connectez-vous sur dropstone.io/dashboard.
  2. Ouvrez Paramètres → API sur dropstone.io/dashboard/settings et créez une clé — elle ressemble à dsk_live_<43 caractères>.
  3. Définissez-la comme variable d'environnement (ou passez apiKey à createDropstoneApi) :
export DROPSTONE_API_KEY=dsk_live_...

Note

Traitez votre clé API comme un mot de passe. Ne la committez jamais dans git et ne l'intégrez pas dans un bundle frontend. Les requêtes avec clé API sont facturées à l'utilisation depuis votre solde de crédit prépayé — les quotas du plan ne s'appliquent pas. Voir Utilisation et limites.

Le streaming fonctionne de la même manière, et le client est compatible OpenAI — vous pouvez pointer le SDK OpenAI vers l'URL de base de Dropstone à la place :

const stream = await dropstone.chat.completions.create({
  model: "dropstone-fast",
  stream: true,
  messages: [{ role: "user", content: "Compte jusqu'à 5." }],
})

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

Consultez la page API HTTP pour la référence complète.


Créer un client

Créez une instance de dropstone :

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

const { client } = await createDropstone()

Cela démarre à la fois un serveur et un client. Chaque méthode SDK retourne { data, request, response }, donc accédez à la charge utile via .data.

Par défaut, le serveur se connecte en tant que celui qui a exécuté dropstone auth login sur cette machine. Sur un hôte sans surveillance, il n'y a pas de tel compte, donc passez une clé API et une liste d'autorisations explicite via 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" },
  },
})

Les deux parties sont requises. Les clés API sont rejetées sur /v1, et l'agent build par défaut demande avant chaque appel d'outil, donc sans liste d'autorisations, l'exécution attend une approbation que personne ne peut donner et se bloque plutôt que d'échouer. Envoyer "agent": "accept all" sur le prompt est l'alternative à la liste d'autorisations. Voir Serveur et Autorisations.

Options

OptionTypeDescriptionDéfaut
hostnamestringNom d'hôte du serveur127.0.0.1
portnumberPort du serveur4096
signalAbortSignalSignal d'annulationundefined
timeoutnumberDélai en ms pour le démarrage5000
configConfigObjet de configuration{}

Configuration

Vous pouvez passer un objet de configuration pour personnaliser le comportement. L'instance récupère toujours votre dropstone.json, mais vous pouvez remplacer ou ajouter de la configuration en ligne :

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

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

console.log(`Serveur en cours d'exécution sur ${dropstone.server.url}`)

dropstone.server.close()

Mémoire (Continuity)

Dropstone conserve une mémoire persistante par compte. Nous l'appelons Continuity — une mémoire transversale partagée par le CLI, le chat, VS Code et le SDK. Enseignez une fois n'importe où et chaque surface la connaît déjà.

Les sessions que vous exécutez via le SDK utilisent la même mémoire de compte que le CLI. Ce que vous enseignez au CLI est déjà connu des sessions SDK, et ce qu'une session SDK enregistre est disponible dans le CLI et le chat. À chaque tour, l'agent rappelle automatiquement la mémoire pertinente avant de répondre, afin de ne pas réapprendre ce qu'il sait déjà.

L'agent SDK peut également lire et écrire la mémoire directement, avec les mêmes outils que le CLI :

OutilObjectif
memory_recallRappeler les leçons les plus pertinentes pour la tâche en cours
record_lessonEnregistrer une leçon durable (une règle ou un fait)
list_lessonsAfficher tout ce qu'il a appris sur vous
forget_lessonSupprimer une leçon

Note

La mémoire nécessite d'être connecté à Dropstone. Le serveur SDK lit le même auth.json que le CLI, donc connectez-vous une fois avec dropstone et les sessions SDK héritent de la même mémoire de compte. Rien n'est stocké si vous n'êtes pas connecté.

Exemple

Exprimez une préférence depuis le code et elle est enregistrée de la même manière que dans le CLI — visible dans le CLI et le chat ensuite :

const { client } = await createDropstone()

const session = await client.session.create({ body: { title: "Enseigner la mémoire" } })

await client.session.prompt({
  path: { id: session.data.id },
  body: {
    parts: [{ type: "text", text: "Retiens ceci comme règle permanente : utilise toujours bun, pas npm." }],
  },
})

Continuity vs AGENTS.md

AGENTS.md est un fichier projet que vous committez dans Git pour les conventions d'équipe — stable, limité au dépôt, et partagé avec quiconque le clone. Continuity est votre mémoire de compte privée : ce que vous enseignez dans le CLI, le chat ou le SDK suit votre compte à travers les surfaces et les projets. Ils résolvent des problèmes différents et fonctionnent mieux ensemble — règles de projet dans AGENTS.md, mémoire personnelle transversale dans Continuity. Voir Règles et Mémoire.

Ce qui est mémorisé

La mémoire stocke uniquement les règles et les faits que vous enseignez explicitement — préférences, conventions et corrections qui valent la peine d'être conservées entre les sessions. Elle est distincte du contexte éphémère d'une session unique, qui n'est pas conservé après la fin de la session.

Consultez la page Mémoire pour comprendre comment Dropstone décide quoi conserver.


Client uniquement

Si vous avez déjà une instance de dropstone en cours d'exécution, vous pouvez créer une instance client pour vous y connecter :

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

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

Options

OptionTypeDescriptionDéfaut
baseUrlstringURL du serveurhttp://localhost:4096
fetchfunctionImplémentation fetch personnaliséeglobalThis.fetch
parseAsstringMéthode d'analyse de la réponseauto
responseStylestringStyle de retour : data ou fieldsfields
throwOnErrorbooleanLever les erreurs au lieu de les retournerfalse

Types

Le SDK inclut des définitions TypeScript pour tous les types d'API. Importez-les directement :

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

Tous les types sont générés à partir de la spécification OpenAPI du serveur, donc les noms que vous voyez en TypeScript correspondent un-à-un aux formes de requête et de réponse du serveur.


Erreurs

Le SDK peut lever des erreurs que vous pouvez attraper et gérer :

try {
  await client.session.get({ path: { id: "invalid-id" } })
} catch (error) {
  console.error("Échec de la récupération de la session :", (error as Error).message)
}

Sortie structurée

Vous pouvez demander une sortie JSON structurée au modèle en spécifiant un format avec un schéma JSON. Le modèle utilisera un outil StructuredOutput pour retourner un JSON validé correspondant à votre schéma.

Utilisation de base

const result = await client.session.prompt({
  path: { id: sessionId },
  body: {
    parts: [{ type: "text", text: "Recherche Dropstone et fournis des informations sur l'entreprise" }],
    format: {
      type: "json_schema",
      schema: {
        type: "object",
        properties: {
          company: { type: "string", description: "Nom de l'entreprise" },
          founded: { type: "number", description: "Année de fondation" },
          products: {
            type: "array",
            items: { type: "string" },
            description: "Produits principaux",
          },
        },
        required: ["company", "founded"],
      },
    },
  },
})

// Accédez à la sortie structurée
console.log(result.data.info.structured_output)
// { company: "Dropstone", founded: 2024, products: ["Dropstone CLI"] }

Types de format de sortie

TypeDescription
textDéfaut. Réponse texte standard (pas de sortie structurée)
json_schemaRetourne un JSON validé correspondant au schéma fourni

Format du schéma JSON

Lorsque vous utilisez type: 'json_schema', fournissez :

ChampTypeDescription
type'json_schema'Requis. Spécifie le mode schéma JSON
schemaobjectRequis. Objet schéma JSON définissant la structure de sortie
retryCountnumberOptionnel. Nombre de tentatives de validation (défaut : 2)

Gestion des erreurs

Si le modèle ne parvient pas à produire une sortie structurée valide après toutes les tentatives, la réponse inclura une StructuredOutputError :

if (result.data.info.error?.name === "StructuredOutputError") {
  console.error("Échec de la production de la sortie structurée :", result.data.info.error.message)
  console.error("Tentatives :", result.data.info.error.retries)
}

Bonnes pratiques

  1. Fournissez des descriptions claires dans les propriétés de votre schéma pour aider le modèle à comprendre quelles données extraire
  2. Utilisez required pour spécifier quels champs doivent être présents
  3. Gardez les schémas ciblés - les schémas imbriqués complexes peuvent être plus difficiles à remplir correctement pour le modèle
  4. Définissez un retryCount approprié - augmentez-le pour les schémas complexes, diminuez-le pour les simples

APIs

Le SDK expose toutes les APIs serveur via un client typé.


Global

MéthodeDescriptionRéponse
global.health()Vérifier la santé et la version du serveur{ healthy: true, version: string }

Exemples

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

App

MéthodeDescriptionRéponse
app.log()Écrire une entrée de journalboolean
app.agents()Lister tous les agents disponiblesAgent[]

Exemples

// Écrire une entrée de journal
await client.app.log({
  body: {
    service: "my-app",
    level: "info",
    message: "Opération terminée",
  },
})

// Lister les agents disponibles
const agents = await client.app.agents()

Projet

MéthodeDescriptionRéponse
project.list()Lister tous les projetsProject[]
project.current()Obtenir le projet actuelProject

Exemples

// Lister tous les projets
const projects = await client.project.list()

// Obtenir le projet actuel
const currentProject = await client.project.current()

Chemin

MéthodeDescriptionRéponse
path.get()Obtenir le chemin actuelPath

Exemples

// Obtenir les informations du chemin actuel
const pathInfo = await client.path.get()

Configuration

MéthodeDescriptionRéponse
config.get()Obtenir les informations de configurationConfig

Exemples

const config = await client.config.get()

Sessions

MéthodeDescriptionNotes
session.list()Lister les sessionsRetourne Session[]
session.get({ path })Obtenir une sessionRetourne Session
session.children({ path })Lister les sessions enfantsRetourne Session[]
session.create({ body })Créer une sessionRetourne Session
session.delete({ path })Supprimer une sessionRetourne boolean
session.update({ path, body })Mettre à jour les propriétés d'une sessionRetourne Session
session.init({ path, body })Analyser l'application et créer AGENTS.mdRetourne boolean
session.abort({ path })Interrompre une session en coursRetourne boolean
session.summarize({ path, body })Résumer une sessionRetourne boolean
session.messages({ path })Lister les messages d'une sessionRetourne { info: Message, parts: Part[]}[]
session.message({ path })Obtenir les détails d'un messageRetourne { info: Message, parts: Part[]}
session.prompt({ path, body })Envoyer un message promptbody.noReply: true retourne UserMessage (contexte uniquement). Par défaut retourne AssistantMessage avec la réponse IA. Prend en charge body.outputFormat pour la sortie structurée
session.command({ path, body })Envoyer une commande à la sessionRetourne { info: AssistantMessage, parts: Part[]}
session.shell({ path, body })Exécuter une commande shellRetourne AssistantMessage
session.revert({ path, body })Annuler un messageRetourne Session
session.unrevert({ path })Restaurer les messages annulésRetourne Session
postSessionByIdPermissionsByPermissionId({ path, body })Répondre à une demande d'autorisationRetourne boolean

Exemples

// Créer et gérer des sessions
const session = await client.session.create({
  body: { title: "Ma session" },
})

const sessions = await client.session.list()

// Envoyer un message prompt
const result = await client.session.prompt({
  path: { id: session.data.id },
  body: {
    model: { providerID: "dropstone", modelID: "dropstone-pro" },
    parts: [{ type: "text", text: "Bonjour !" }],
  },
})

// Injecter du contexte sans déclencher de réponse IA (utile pour les plugins)
await client.session.prompt({
  path: { id: session.data.id },
  body: {
    noReply: true,
    parts: [{ type: "text", text: "Vous êtes un assistant utile." }],
  },
})

Fichiers

MéthodeDescriptionRéponse
find.text({ query })Rechercher du texte dans les fichiersTableau d'objets de correspondance avec path, lines, line_number, absolute_offset, submatches
find.files({ query })Trouver des fichiers et répertoires par nomstring[] (chemins)
find.symbols({ query })Trouver des symboles d'espace de travailSymbol[]
file.read({ query })Lire un fichier{ type: "raw" | "patch", content: string }
file.status({ query? })Obtenir le statut des fichiers suivisFile[]

find.files prend en charge quelques champs de requête optionnels :

  • type : "file" ou "directory"
  • directory : remplace la racine du projet pour la recherche
  • limit : nombre maximal de résultats (1–200)

Exemples

// Rechercher et lire des fichiers
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éthodeDescriptionRéponse
auth.set({ ... })Définir les informations d'authentificationboolean

Exemples

await client.auth.set({
  path: { id: "dropstone" },
  body: { type: "api", key: "votre-clé-api-dropstone" },
})

Événements

MéthodeDescriptionRéponse
event.subscribe()Flux d'événements envoyés par le serveurFlux d'événements envoyés par le serveur

Exemples

// Écouter les événements en temps réel
const events = await client.event.subscribe()
for await (const event of events.stream) {
  console.log("Événement :", event.type, event.properties)
}

API v2

Le SDK fournit une surface v1 stable (utilisée dans les exemples ci-dessus) et une surface v2 qui reflète le contrat Effect HttpApi plus récent. Privilégiez v2 pour les nouvelles intégrations :

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

const { client, server } = await createDropstone()
// v2 expose une hiérarchie de ressources plus riche : workspace, worktree, file, find, etc.
const files = await client.file.list({ path: "src" })

La surface v1 est conservée pour la rétrocompatibilité. Les nouveaux points de terminaison n'arrivent que sur v2.

Ctrl+I