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) // 計費金額,以美元計
取得 API 金鑰
- 在 dropstone.io/dashboard 登入。
- 在 dropstone.io/dashboard/settings 開啟 設定 → API 並建立金鑰——格式類似
dsk_live_<43 個字元>。 - 將其設定為環境變數(或將
apiKey傳遞給createDropstoneApi):
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 | 伺服器啟動的逾時時間(毫秒) | 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 與 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 schema 的 format 來要求模型輸出結構化的 JSON。模型會使用 StructuredOutput 工具回傳符合您 schema 的驗證 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 | 回傳符合提供 schema 的驗證 JSON |
JSON Schema 格式
使用 type: 'json_schema' 時,請提供:
| 欄位 | 類型 | 描述 |
|---|---|---|
type | 'json_schema' | 必填。指定 JSON schema 模式 |
schema | object | 必填。定義輸出結構的 JSON Schema 物件 |
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)
}
最佳實務
- 在 schema 屬性中提供清晰的描述,以協助模型了解要擷取什麼資料
- 使用
required指定哪些欄位必須存在 - 保持 schema 聚焦 - 複雜的巢狀 schema 可能讓模型更難正確填寫
- 設定適當的
retryCount- 複雜 schema 增加,簡單 schema 減少
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(僅內容)。預設回傳 AssistantMessage 及 AI 回應。支援 body.outputFormat 以進行結構化輸出 |
session.command({ path, body }) | 傳送指令給工作階段 | 回傳 { info: AssistantMessage, parts: Part[]} |
session.shell({ path, body }) | 執行 shell 指令 | 回傳 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。