Dropstone SDK
Dropstone 에이전트 런타임을 위한 타입 안전 JS 클라이언트입니다. SDK 세션은 Dropstone의 표면 간 메모리(Continuity)를 상속합니다 — CLI, 채팅, SDK와 공유되는 하나의 영구 메모리입니다.
Dropstone JS/TS SDK는 Dropstone 에이전트 런타임과 상호작용하기 위한 타입 안전 클라이언트를 제공합니다. dropstone serve를 하위 프로세스로 실행하고, 이를 가리키는 타입이 지정된 HTTP 클라이언트를 제공합니다. CLI가 사용하는 것과 동일한 로컬 에이전트를 실행하기 때문에 모든 SDK 세션은 계정 메모리를 상속합니다 — CLI, 채팅, SDK 중 한 곳에서 한 번만 가르치면 모든 표면에서 이미 알고 있습니다 (메모리 (Continuity) 참조).
CI에서 헤드리스 API 액세스가 필요하신가요?
순수 프로그래밍 방식 사용(CI 파이프라인, 자동화, 서버리스)의 경우 DROPSTONE_API_KEY와 함께 HTTP API를 사용하는 것이 좋습니다. 이 페이지의 SDK는 CLI 바이너리가 함께 설치된 Node 프로세스에 대화형 에이전트를 내장하기 위한 것입니다.
기본 HTTP API의 작동 방식은 서버 페이지를 참조하세요.
설치
npm에서 SDK를 설치합니다:
npm install @blankline/dropstone-sdk
헤드리스 API 클라이언트
CI 파이프라인, 자동화, 서버리스의 경우 CLI가 전혀 필요하지 않습니다. API 키로 Dropstone HTTP 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) // 청구 금액 (USD)
API 키 받기
- dropstone.io/dashboard에서 로그인합니다.
- dropstone.io/dashboard/settings에서 설정 → API를 열고 키를 생성합니다 —
dsk_live_<43자>형태입니다. - 환경 변수로 설정합니다 (또는
createDropstoneApi에apiKey를 전달):
export DROPSTONE_API_KEY=dsk_live_...
Note
API 키를 비밀번호처럼 취급하세요. Git에 커밋하거나 프론트엔드 번들에 포함하지 마세요. API 키 요청은 선불 크레딧 잔액에서 종량제로 청구됩니다 — 플랜 허용량은 적용되지 않습니다. 사용량 및 제한을 참조하세요.
스트리밍도 동일한 방식으로 작동하며, 클라이언트는 OpenAI 호환입니다 — OpenAI SDK를 Dropstone의 기본 URL에 지정할 수도 있습니다:
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을 실행한 사용자로 로그인합니다. 무인 호스트에는 해당 계정이 없으므로 config를 통해 API 키와 명시적 권한 허용 목록을 전달합니다:
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 | 서버 시작 대기 시간(ms) | 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 서버는 CLI와 동일한 auth.json을 읽으므로 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 vs 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에는 모든 API 타입에 대한 TypeScript 정의가 포함되어 있습니다. 직접 가져올 수 있습니다:
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.health() | 서버 상태 및 버전 확인 | { healthy: true, version: string } |
예시
const health = await client.global.health()
console.log(health.data.version)
앱
| 메서드 | 설명 | 응답 |
|---|---|---|
app.log() | 로그 항목 작성 | boolean |
app.agents() | 사용 가능한 모든 에이전트 나열 | Agent[] |
예시
// 로그 항목 작성
await client.app.log({
body: {
service: "my-app",
level: "info",
message: "작업 완료",
},
})
// 사용 가능한 에이전트 나열
const agents = await client.app.agents()
프로젝트
| 메서드 | 설명 | 응답 |
|---|---|---|
project.list() | 모든 프로젝트 나열 | Project[] |
project.current() | 현재 프로젝트 가져오기 | Project |
예시
// 모든 프로젝트 나열
const projects = await client.project.list()
// 현재 프로젝트 가져오기
const currentProject = await client.project.current()
경로
| 메서드 | 설명 | 응답 |
|---|---|---|
path.get() | 현재 경로 가져오기 | Path |
예시
// 현재 경로 정보 가져오기
const pathInfo = await client.path.get()
구성
| 메서드 | 설명 | 응답 |
|---|---|---|
config.get() | 구성 정보 가져오기 | Config |
예시
const config = await client.config.get()
세션
| 메서드 | 설명 | 참고 |
|---|---|---|
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 반환 (컨텍스트만). 기본값은 AI 응답이 포함된 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: "안녕하세요!" }],
},
})
// AI 응답 없이 컨텍스트 주입 (플러그인에 유용)
await client.session.prompt({
path: { id: session.data.id },
body: {
noReply: true,
parts: [{ type: "text", text: "당신은 유용한 비서입니다." }],
},
})
파일
| 메서드 | 설명 | 응답 |
|---|---|---|
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.set({ ... }) | 인증 자격 증명 설정 | boolean |
예시
await client.auth.set({
path: { id: "dropstone" },
body: { type: "api", key: "your-dropstone-api-key" },
})
이벤트
| 메서드 | 설명 | 응답 |
|---|---|---|
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 표면(위 예시에서 사용)과 최신 Effect HttpApi 계약을 반영한 v2 표면을 제공합니다. 새 통합에는 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에만 추가됩니다.