Dropstone CLI

Dropstone SDK

Typsicherer JS-Client für die Dropstone-Agentenlaufzeit. SDK-Sitzungen erben Dropstones oberflächenübergreifenden Speicher (Continuity) – ein persistenter Speicher, der mit CLI, Chat und SDK geteilt wird.

Das Dropstone JS/TS SDK bietet einen typsicheren Client für die Interaktion mit der Dropstone-Agentenlaufzeit. Es startet dropstone serve als Unterprozess und gibt dir einen typisierten HTTP-Client, der darauf zeigt. Da es denselben lokalen Agenten verwendet wie die CLI, erbt jede SDK-Sitzung deinen Kontospeicher – einmal in der CLI, im Chat oder im SDK beigebracht, kennt ihn jede Oberfläche bereits (siehe Speicher (Continuity)).

Du brauchst Headless-API-Zugriff in CI?

Für rein programmatische Nutzung (CI-Pipelines, Automatisierung, Serverless) bevorzuge die HTTP-API mit einem DROPSTONE_API_KEY. Das SDK auf dieser Seite ist dafür gedacht, den interaktiven Agenten in einen Node-Prozess einzubetten, in dem die CLI-Binary mitinstalliert ist.

Siehe die Seite Server für Details zur zugrunde liegenden HTTP-API.


Installation

Installiere das SDK von npm:

npm install @blankline/dropstone-sdk

Headless-API-Client

Für CI-Pipelines, Automatisierung und Serverless brauchst du die CLI überhaupt nicht. Verwende den Headless-Client, der direkt mit einem API-Schlüssel mit der Dropstone-HTTP-API spricht:

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

// Liest DROPSTONE_API_KEY automatisch aus der Umgebung.
const dropstone = createDropstoneApi()

const resp = await dropstone.chat.completions.create({
  model: "dropstone-fast", // oder "dropstone-pro" / "dropstone-heavy"
  messages: [{ role: "user", content: "Schreibe ein Haiku über das Debuggen." }],
})

console.log(resp.choices[0].message.content)
console.log("Kosten: $" + resp.usage?.cost) // abgerechneter Betrag in USD

API-Schlüssel erhalten

  1. Melde dich bei dropstone.io/dashboard an.
  2. Öffne Einstellungen → API unter dropstone.io/dashboard/settings und erstelle einen Schlüssel – er sieht aus wie dsk_live_<43 Zeichen>.
  3. Setze ihn als Umgebungsvariable (oder übergib apiKey an createDropstoneApi):
export DROPSTONE_API_KEY=dsk_live_...

Note

Behandle deinen API-Schlüssel wie ein Passwort. Committe ihn niemals in Git und bette ihn nicht in ein Frontend-Bundle ein. API-Schlüssel-Anfragen werden nutzungsbasiert von deinem Prepaid-Guthaben abgerechnet – Plan-Kontingente gelten nicht. Siehe Nutzung & Limits.

Streaming funktioniert auf dieselbe Weise, und der Client ist OpenAI-kompatibel – du kannst das OpenAI SDK stattdessen auf die Basis-URL von Dropstone zeigen lassen:

const stream = await dropstone.chat.completions.create({
  model: "dropstone-fast",
  stream: true,
  messages: [{ role: "user", content: "Zähle bis 5." }],
})

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

Siehe die Seite HTTP-API für die vollständige Referenz.


Client erstellen

Erstelle eine Instanz von dropstone:

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

const { client } = await createDropstone()

Dies startet sowohl einen Server als auch einen Client. Jede SDK-Methode gibt { data, request, response } zurück, also greife über .data auf die Nutzdaten zu.

Standardmäßig meldet sich der Server als derjenige an, der dropstone auth login auf dieser Maschine ausgeführt hat. Auf einem unbeaufsichtigten Host gibt es kein solches Konto, also übergib einen API-Schlüssel und eine explizite Berechtigungs-Allowlist über 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" },
  },
})

Beide Teile sind erforderlich. API-Schlüssel werden bei /v1 abgelehnt, und der Standard-build-Agent fragt vor jedem Tool-Aufruf nach, sodass der Lauf ohne Allowlist auf eine Genehmigung wartet, die niemand geben kann, und hängt statt fehlzuschlagen. "agent": "accept all" auf dem Prompt zu senden ist die Alternative zur Allowlist. Siehe Server und Berechtigungen.

Optionen

OptionTypBeschreibungStandard
hostnamestringServer-Hostname127.0.0.1
portnumberServer-Port4096
signalAbortSignalAbbruchsignal für die Stornierungundefined
timeoutnumberTimeout in ms für den Serverstart5000
configConfigKonfigurationsobjekt{}

Konfiguration

Du kannst ein Konfigurationsobjekt übergeben, um das Verhalten anzupassen. Die Instanz übernimmt weiterhin deine dropstone.json, aber du kannst Konfiguration inline überschreiben oder ergänzen:

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

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

console.log(`Server läuft unter ${dropstone.server.url}`)

dropstone.server.close()

Speicher (Continuity)

Dropstone führt einen persistenten Speicher pro Konto. Wir nennen ihn Continuity – einen oberflächenübergreifenden Speicher, der von CLI, Chat, VS Code und dem SDK geteilt wird. Einmal irgendwo beigebracht, kennt ihn jede Oberfläche bereits.

Sitzungen, die du über das SDK ausführst, verwenden denselben Kontospeicher wie die CLI. Was du der CLI beibringst, ist SDK-Sitzungen bereits bekannt, und was eine SDK-Sitzung aufzeichnet, ist wieder in CLI und Chat verfügbar. Bei jeder Runde ruft der Agent automatisch relevanten Speicher ab, bevor er antwortet, sodass er nicht neu lernt, was er bereits weiß.

Der SDK-Agent kann Speicher auch direkt lesen und schreiben, mit denselben Tools wie die CLI:

ToolZweck
memory_recallDie relevantesten Lektionen für die aktuelle Aufgabe abrufen
record_lessonEine dauerhafte Lektion speichern (eine Regel oder eine Tatsache)
edit_lessonEine gespeicherte Lektion an Ort und Stelle umschreiben, wenn sie veraltet oder schlecht formuliert ist
list_lessonsAlles anzeigen, was über dich gelernt wurde
forget_lessonEine Lektion entfernen

Note

Speicher erfordert, dass du bei Dropstone angemeldet bist. Der SDK-Server liest dieselbe auth.json wie die CLI, also melde dich einmal mit dropstone an, und SDK-Sitzungen erben denselben Kontospeicher. Wenn du nicht angemeldet bist, wird nichts gespeichert.

Beispiel

Gib eine Präferenz aus Code an, und sie wird auf dieselbe Weise aufgezeichnet wie in der CLI – danach in CLI und Chat sichtbar:

const { client } = await createDropstone()

const session = await client.session.create({ body: { title: "Speicher beibringen" } })

await client.session.prompt({
  path: { id: session.data.id },
  body: {
    parts: [{ type: "text", text: "Merke dir das als feste Regel: verwende immer bun, nicht npm." }],
  },
})

Continuity vs. AGENTS.md

AGENTS.md ist eine Projektdatei, die du für Teamkonventionen in Git committest – stabil, repo-bezogen und für alle geteilt, die sie klonen. Continuity ist dein privater Kontospeicher: Was du in CLI, Chat oder SDK beibringst, folgt deinem Konto über Oberflächen und Projekte hinweg. Sie lösen unterschiedliche Probleme und funktionieren am besten zusammen – Projektregeln in AGENTS.md, persönlicher oberflächenübergreifender Speicher in Continuity. Siehe Regeln und Speicher.

Was gespeichert wird

Der Speicher enthält nur die Regeln und Tatsachen, die du ihm explizit beibringst – Präferenzen, Konventionen und Korrekturen, die es wert sind, über Sitzungen hinweg mitgenommen zu werden. Er unterscheidet sich vom flüchtigen Kontext einer einzelnen Sitzung, der nach Sitzungsende nicht aufbewahrt wird.

Siehe die Seite Speicher für Details, wie Dropstone entscheidet, was behalten wird.


Nur Client

Wenn du bereits eine laufende Instanz von dropstone hast, kannst du eine Client-Instanz erstellen, um dich mit ihr zu verbinden:

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

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

Optionen

OptionTypBeschreibungStandard
baseUrlstringURL des Servershttp://localhost:4096
fetchfunctionBenutzerdefinierte Fetch-ImplementierungglobalThis.fetch
parseAsstringAntwort-Parsing-Methodeauto
responseStylestringRückgabestil: data oder fieldsfields
throwOnErrorbooleanFehler werfen statt zurückgebenfalse

Typen

Das SDK enthält TypeScript-Definitionen für alle API-Typen. Importiere sie direkt:

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

Alle Typen werden aus der OpenAPI-Spezifikation des Servers generiert, sodass die Namen, die du in TypeScript siehst, eins-zu-eins auf die Server-Anfrage- und Antwortstrukturen abbilden.


Fehler

Das SDK kann Fehler werfen, die du abfangen und behandeln kannst:

try {
  await client.session.get({ path: { id: "invalid-id" } })
} catch (error) {
  console.error("Sitzung konnte nicht abgerufen werden:", (error as Error).message)
}

Strukturierte Ausgabe

Du kannst strukturierte JSON-Ausgabe vom Modell anfordern, indem du ein format mit einem JSON-Schema angibst. Das Modell verwendet ein StructuredOutput-Tool, um validiertes JSON zurückzugeben, das deinem Schema entspricht.

Grundlegende Verwendung

const result = await client.session.prompt({
  path: { id: sessionId },
  body: {
    parts: [{ type: "text", text: "Recherchiere Dropstone und gib Firmeninformationen an" }],
    format: {
      type: "json_schema",
      schema: {
        type: "object",
        properties: {
          company: { type: "string", description: "Firmenname" },
          founded: { type: "number", description: "Gründungsjahr" },
          products: {
            type: "array",
            items: { type: "string" },
            description: "Hauptprodukte",
          },
        },
        required: ["company", "founded"],
      },
    },
  },
})

// Auf die strukturierte Ausgabe zugreifen
console.log(result.data.info.structured_output)
// { company: "Dropstone", founded: 2024, products: ["Dropstone CLI"] }

Ausgabeformat-Typen

TypBeschreibung
textStandard. Normale Textantwort (keine strukturierte Ausgabe)
json_schemaGibt validiertes JSON zurück, das dem bereitgestellten Schema entspricht

JSON-Schema-Format

Bei Verwendung von type: 'json_schema' angeben:

FeldTypBeschreibung
type'json_schema'Erforderlich. Gibt den JSON-Schema-Modus an
schemaobjectErforderlich. JSON-Schema-Objekt, das die Ausgabestruktur definiert
retryCountnumberOptional. Anzahl der Validierungswiederholungen (Standard: 2)

Fehlerbehandlung

Wenn das Modell nach allen Wiederholungen keine gültige strukturierte Ausgabe erzeugt, enthält die Antwort einen StructuredOutputError:

if (result.data.info.error?.name === "StructuredOutputError") {
  console.error("Strukturierte Ausgabe fehlgeschlagen:", result.data.info.error.message)
  console.error("Versuche:", result.data.info.error.retries)
}

Best Practices

  1. Gib klare Beschreibungen in deinen Schema-Eigenschaften an, damit das Modell versteht, welche Daten extrahiert werden sollen
  2. Verwende required, um anzugeben, welche Felder vorhanden sein müssen
  3. Halte Schemas fokussiert – komplexe verschachtelte Schemas können für das Modell schwerer korrekt auszufüllen sein
  4. Setze einen angemessenen retryCount – erhöhe ihn für komplexe Schemas, verringere ihn für einfache

APIs

Das SDK stellt alle Server-APIs über einen typsicheren Client bereit.


Global

MethodeBeschreibungAntwort
global.health()Server-Gesundheit und Version prüfen{ healthy: true, version: string }

Beispiele

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

App

MethodeBeschreibungAntwort
app.log()Einen Protokolleintrag schreibenboolean
app.agents()Alle verfügbaren Agenten auflistenAgent[]

Beispiele

// Einen Protokolleintrag schreiben
await client.app.log({
  body: {
    service: "my-app",
    level: "info",
    message: "Vorgang abgeschlossen",
  },
})

// Verfügbare Agenten auflisten
const agents = await client.app.agents()

Projekt

MethodeBeschreibungAntwort
project.list()Alle Projekte auflistenProject[]
project.current()Aktuelles Projekt abrufenProject

Beispiele

// Alle Projekte auflisten
const projects = await client.project.list()

// Aktuelles Projekt abrufen
const currentProject = await client.project.current()

Pfad

MethodeBeschreibungAntwort
path.get()Aktuellen Pfad abrufenPath

Beispiele

// Aktuelle Pfadinformationen abrufen
const pathInfo = await client.path.get()

Konfiguration

MethodeBeschreibungAntwort
config.get()Konfigurationsinfo abrufenConfig

Beispiele

const config = await client.config.get()

Sitzungen

MethodeBeschreibungHinweise
session.list()Sitzungen auflistenGibt Session[] zurück
session.get({ path })Sitzung abrufenGibt Session zurück
session.children({ path })Untersitzungen auflistenGibt Session[] zurück
session.create({ body })Sitzung erstellenGibt Session zurück
session.delete({ path })Sitzung löschenGibt boolean zurück
session.update({ path, body })Sitzungseigenschaften aktualisierenGibt Session zurück
session.init({ path, body })App analysieren und AGENTS.md erstellenGibt boolean zurück
session.abort({ path })Laufende Sitzung abbrechenGibt boolean zurück
session.summarize({ path, body })Sitzung zusammenfassenGibt boolean zurück
session.messages({ path })Nachrichten in einer Sitzung auflistenGibt { info: Message, parts: Part[]}[] zurück
session.message({ path })Nachrichtendetails abrufenGibt { info: Message, parts: Part[]} zurück
session.prompt({ path, body })Prompt-Nachricht sendenbody.noReply: true gibt UserMessage zurück (nur Kontext). Standardmäßig wird AssistantMessage mit KI-Antwort zurückgegeben. Unterstützt body.outputFormat für strukturierte Ausgabe
session.command({ path, body })Befehl an Sitzung sendenGibt { info: AssistantMessage, parts: Part[]} zurück
session.shell({ path, body })Shell-Befehl ausführenGibt AssistantMessage zurück
session.revert({ path, body })Eine Nachricht zurücknehmenGibt Session zurück
session.unrevert({ path })Zurückgenommene Nachrichten wiederherstellenGibt Session zurück
postSessionByIdPermissionsByPermissionId({ path, body })Auf Berechtigungsanfrage antwortenGibt boolean zurück

Beispiele

// Sitzungen erstellen und verwalten
const session = await client.session.create({
  body: { title: "Meine Sitzung" },
})

const sessions = await client.session.list()

// Eine Prompt-Nachricht senden
const result = await client.session.prompt({
  path: { id: session.data.id },
  body: {
    model: { providerID: "dropstone", modelID: "dropstone-pro" },
    parts: [{ type: "text", text: "Hallo!" }],
  },
})

// Kontext injizieren, ohne KI-Antwort auszulösen (nützlich für Plugins)
await client.session.prompt({
  path: { id: session.data.id },
  body: {
    noReply: true,
    parts: [{ type: "text", text: "Du bist ein hilfreicher Assistent." }],
  },
})

Dateien

MethodeBeschreibungAntwort
find.text({ query })Text in Dateien suchenArray von Trefferobjekten mit path, lines, line_number, absolute_offset, submatches
find.files({ query })Dateien und Verzeichnisse nach Name findenstring[] (Pfade)
find.symbols({ query })Workspace-Symbole findenSymbol[]
file.read({ query })Eine Datei lesen{ type: "raw" | "patch", content: string }
file.status({ query? })Status für verfolgte Dateien abrufenFile[]

find.files unterstützt einige optionale Abfragefelder:

  • type: "file" oder "directory"
  • directory: Projektstamm für die Suche überschreiben
  • limit: maximale Ergebnisse (1–200)

Beispiele

// Dateien suchen und lesen
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

MethodeBeschreibungAntwort
auth.set({ ... })Anmeldeinformationen festlegenboolean

Beispiele

await client.auth.set({
  path: { id: "dropstone" },
  body: { type: "api", key: "dein-dropstone-api-schlüssel" },
})

Ereignisse

MethodeBeschreibungAntwort
event.subscribe()Server-Sent-Events-StreamServer-Sent-Events-Stream

Beispiele

// Auf Echtzeit-Ereignisse hören
const events = await client.event.subscribe()
for await (const event of events.stream) {
  console.log("Ereignis:", event.type, event.properties)
}

v2-API

Das SDK enthält eine stabile v1-Oberfläche (in den obigen Beispielen verwendet) und eine v2-Oberfläche, die den neueren Effect-HttpApi-Vertrag widerspiegelt. Bevorzuge v2 für neue Integrationen:

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

const { client, server } = await createDropstone()
// v2 bietet eine reichhaltigere Ressourcenhierarchie: workspace, worktree, file, find usw.
const files = await client.file.list({ path: "src" })

Die v1-Oberfläche wird aus Gründen der Abwärtskompatibilität beibehalten. Neue Endpunkte landen nur auf v2.

Strg+I