@blankline/dropstone-sdk 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +146 -0
- package/dist/api.d.ts +118 -0
- package/dist/api.js +125 -0
- package/dist/client.d.ts +7 -0
- package/dist/client.js +51 -0
- package/dist/gen/client/client.gen.d.ts +2 -0
- package/dist/gen/client/client.gen.js +165 -0
- package/dist/gen/client/index.d.ts +7 -0
- package/dist/gen/client/index.js +5 -0
- package/dist/gen/client/types.gen.d.ts +127 -0
- package/dist/gen/client/types.gen.js +2 -0
- package/dist/gen/client/utils.gen.d.ts +38 -0
- package/dist/gen/client/utils.gen.js +226 -0
- package/dist/gen/client.gen.d.ts +12 -0
- package/dist/gen/client.gen.js +5 -0
- package/dist/gen/core/auth.gen.d.ts +18 -0
- package/dist/gen/core/auth.gen.js +14 -0
- package/dist/gen/core/bodySerializer.gen.d.ts +17 -0
- package/dist/gen/core/bodySerializer.gen.js +57 -0
- package/dist/gen/core/params.gen.d.ts +33 -0
- package/dist/gen/core/params.gen.js +89 -0
- package/dist/gen/core/pathSerializer.gen.d.ts +33 -0
- package/dist/gen/core/pathSerializer.gen.js +106 -0
- package/dist/gen/core/queryKeySerializer.gen.d.ts +18 -0
- package/dist/gen/core/queryKeySerializer.gen.js +93 -0
- package/dist/gen/core/serverSentEvents.gen.d.ts +59 -0
- package/dist/gen/core/serverSentEvents.gen.js +117 -0
- package/dist/gen/core/types.gen.d.ts +78 -0
- package/dist/gen/core/types.gen.js +2 -0
- package/dist/gen/core/utils.gen.d.ts +14 -0
- package/dist/gen/core/utils.gen.js +69 -0
- package/dist/gen/sdk.gen.d.ts +403 -0
- package/dist/gen/sdk.gen.js +881 -0
- package/dist/gen/types.gen.d.ts +3380 -0
- package/dist/gen/types.gen.js +2 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +17 -0
- package/dist/process.d.ts +3 -0
- package/dist/process.js +33 -0
- package/dist/server.d.ts +23 -0
- package/dist/server.js +105 -0
- package/dist/v2/client.d.ts +8 -0
- package/dist/v2/client.js +96 -0
- package/dist/v2/data.d.ts +9 -0
- package/dist/v2/data.js +22 -0
- package/dist/v2/gen/client/client.gen.d.ts +2 -0
- package/dist/v2/gen/client/client.gen.js +232 -0
- package/dist/v2/gen/client/index.d.ts +8 -0
- package/dist/v2/gen/client/index.js +6 -0
- package/dist/v2/gen/client/types.gen.d.ts +117 -0
- package/dist/v2/gen/client/types.gen.js +2 -0
- package/dist/v2/gen/client/utils.gen.d.ts +33 -0
- package/dist/v2/gen/client/utils.gen.js +226 -0
- package/dist/v2/gen/client.gen.d.ts +12 -0
- package/dist/v2/gen/client.gen.js +3 -0
- package/dist/v2/gen/core/auth.gen.d.ts +18 -0
- package/dist/v2/gen/core/auth.gen.js +14 -0
- package/dist/v2/gen/core/bodySerializer.gen.d.ts +25 -0
- package/dist/v2/gen/core/bodySerializer.gen.js +57 -0
- package/dist/v2/gen/core/params.gen.d.ts +43 -0
- package/dist/v2/gen/core/params.gen.js +102 -0
- package/dist/v2/gen/core/pathSerializer.gen.d.ts +33 -0
- package/dist/v2/gen/core/pathSerializer.gen.js +106 -0
- package/dist/v2/gen/core/queryKeySerializer.gen.d.ts +18 -0
- package/dist/v2/gen/core/queryKeySerializer.gen.js +93 -0
- package/dist/v2/gen/core/serverSentEvents.gen.d.ts +71 -0
- package/dist/v2/gen/core/serverSentEvents.gen.js +133 -0
- package/dist/v2/gen/core/types.gen.d.ts +78 -0
- package/dist/v2/gen/core/types.gen.js +2 -0
- package/dist/v2/gen/core/utils.gen.d.ts +19 -0
- package/dist/v2/gen/core/utils.gen.js +87 -0
- package/dist/v2/gen/sdk.gen.d.ts +1549 -0
- package/dist/v2/gen/sdk.gen.js +3146 -0
- package/dist/v2/gen/types.gen.d.ts +5749 -0
- package/dist/v2/gen/types.gen.js +2 -0
- package/dist/v2/index.d.ts +11 -0
- package/dist/v2/index.js +17 -0
- package/dist/v2/server.d.ts +23 -0
- package/dist/v2/server.js +105 -0
- package/package.json +86 -0
package/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# @blankline/dropstone-sdk
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for [Dropstone](https://dropstone.io). Two ways to use it:
|
|
4
|
+
|
|
5
|
+
1. **Headless HTTP API client** — for CI, automation, serverless, and any context without the Dropstone CLI installed. Uses an API key, talks directly to `https://api.dropstone.io/api/v1`.
|
|
6
|
+
2. **Local agent client** — spawns `dropstone serve` as a subprocess and gives you a typed client against the full local agent surface (sessions, file ops, tools). Use when embedding the interactive agent in a Node app.
|
|
7
|
+
|
|
8
|
+
> **Status:** preview. The API surface may shift before `1.0.0`. Pin the version range you depend on.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @blankline/dropstone-sdk
|
|
14
|
+
# or
|
|
15
|
+
pnpm add @blankline/dropstone-sdk
|
|
16
|
+
# or
|
|
17
|
+
bun add @blankline/dropstone-sdk
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
For the **headless API client**, you only need the npm package and a `DROPSTONE_API_KEY` (generate one at [dropstone.io/dashboard/settings](https://dropstone.io/dashboard/settings)).
|
|
21
|
+
|
|
22
|
+
For the **local agent client**, you also need the Dropstone CLI installed on the same machine:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
brew install blankline-org/dropstone-cli/dropstone # macOS / Linux
|
|
26
|
+
irm https://dropstone.io/install.ps1 | iex # Windows
|
|
27
|
+
curl -fsSL https://dropstone.io/install | bash # Linux (bash)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Headless HTTP API client (recommended for CI / automation)
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { createDropstoneApi } from "@blankline/dropstone-sdk"
|
|
34
|
+
|
|
35
|
+
// Reads DROPSTONE_API_KEY from env automatically.
|
|
36
|
+
const dropstone = createDropstoneApi()
|
|
37
|
+
|
|
38
|
+
const resp = await dropstone.chat.completions.create({
|
|
39
|
+
model: "dropstone-fast", // or "dropstone-pro" / "dropstone-heavy"
|
|
40
|
+
messages: [
|
|
41
|
+
{ role: "user", content: "Write a haiku about debugging." },
|
|
42
|
+
],
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
console.log(resp.choices[0].message.content)
|
|
46
|
+
console.log("Cost: $" + resp.usage?.cost) // billed amount, in USD
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Streaming:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
const stream = await dropstone.chat.completions.create({
|
|
53
|
+
model: "dropstone-fast",
|
|
54
|
+
stream: true,
|
|
55
|
+
messages: [{ role: "user", content: "Count to 5." }],
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
for await (const chunk of stream) {
|
|
59
|
+
process.stdout.write(chunk.choices?.[0]?.delta?.content ?? "")
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The API client is also drop-in OpenAI-compatible — if you'd rather use the OpenAI SDK, override its `baseURL`:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import OpenAI from "openai"
|
|
67
|
+
const openai = new OpenAI({
|
|
68
|
+
baseURL: "https://api.dropstone.io/api/v1",
|
|
69
|
+
apiKey: process.env.DROPSTONE_API_KEY,
|
|
70
|
+
})
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Full HTTP API reference: [docs.dropstone.io/cli/api](https://docs.dropstone.io/cli/api).
|
|
74
|
+
|
|
75
|
+
## Local agent client
|
|
76
|
+
|
|
77
|
+
Spin up a local Dropstone server and send it a chat prompt:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { createDropstone } from "@blankline/dropstone-sdk"
|
|
81
|
+
|
|
82
|
+
const { client, server } = await createDropstone()
|
|
83
|
+
|
|
84
|
+
const session = await client.session.create.mutate({
|
|
85
|
+
body: { agent: "build", model: { providerID: "dropstone", id: "dropstone-pro" } },
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
const reply = await client.session.prompt.mutate({
|
|
89
|
+
path: { id: session.data.id },
|
|
90
|
+
body: { parts: [{ type: "text", text: "Hello, who are you?" }] },
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
console.log(reply.data)
|
|
94
|
+
|
|
95
|
+
await server.close()
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`createDropstone` launches a fresh `dropstone serve` subprocess bound to `127.0.0.1` on a free port, returns a typed HTTP client pointed at it, and a `close()` to tear it down.
|
|
99
|
+
|
|
100
|
+
## Talking to an already-running server
|
|
101
|
+
|
|
102
|
+
If you already have a Dropstone server running (or want to point at a remote one), use `createDropstoneClient` directly:
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
import { createDropstoneClient } from "@blankline/dropstone-sdk"
|
|
106
|
+
|
|
107
|
+
const client = createDropstoneClient({ baseUrl: "http://127.0.0.1:4096" })
|
|
108
|
+
const config = await client.config.get.query()
|
|
109
|
+
console.log(config.data)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## v2 API
|
|
113
|
+
|
|
114
|
+
The SDK has both a stable v1 surface (auto-generated from the legacy Hono routes) and a v2 surface that mirrors the new Effect HttpApi contract. Prefer v2 for new code:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
import { createDropstone } from "@blankline/dropstone-sdk/v2"
|
|
118
|
+
|
|
119
|
+
const { client, server } = await createDropstone()
|
|
120
|
+
// v2 exposes a richer resource hierarchy: workspace, worktree, file, find, etc.
|
|
121
|
+
const files = await client.file.list.query({ path: { worktreeID: "default" } })
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The v1 surface is kept for backward compatibility with existing integrations. New endpoints (`/api/usage`, PKCE auth, workspace adapters) only land on v2.
|
|
125
|
+
|
|
126
|
+
## TUI
|
|
127
|
+
|
|
128
|
+
You can also launch the Dropstone interactive TUI from a Node process:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
import { createDropstoneTui } from "@blankline/dropstone-sdk"
|
|
132
|
+
|
|
133
|
+
const tui = createDropstoneTui({ model: "dropstone-pro" })
|
|
134
|
+
// ... when you're done:
|
|
135
|
+
tui.close()
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The TUI takes over stdin/stdout/stderr inherited from the parent process.
|
|
139
|
+
|
|
140
|
+
## TypeScript
|
|
141
|
+
|
|
142
|
+
The SDK ships full type definitions generated from the server's OpenAPI spec via `@hey-api/openapi-ts`. All request bodies, query params, and responses are typed end-to-end — your editor will autocomplete every endpoint.
|
|
143
|
+
|
|
144
|
+
## License
|
|
145
|
+
|
|
146
|
+
MIT
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
export type DropstoneTier = "dropstone-fast" | "dropstone-pro" | "dropstone-heavy";
|
|
2
|
+
export interface DropstoneApiOptions {
|
|
3
|
+
/**
|
|
4
|
+
* API key in the form `dsk_live_...`. Generate one at
|
|
5
|
+
* https://dropstone.io/dashboard/settings (API tab).
|
|
6
|
+
* Falls back to `process.env.DROPSTONE_API_KEY` if omitted.
|
|
7
|
+
*/
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Override the base URL. Default `https://api.dropstone.io/api/v1`.
|
|
11
|
+
* Only useful for testing against a local server.
|
|
12
|
+
*/
|
|
13
|
+
baseUrl?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Custom fetch — useful for retries / instrumentation / proxies.
|
|
16
|
+
*/
|
|
17
|
+
fetch?: typeof fetch;
|
|
18
|
+
}
|
|
19
|
+
export interface ChatMessage {
|
|
20
|
+
role: "system" | "user" | "assistant" | "tool";
|
|
21
|
+
content: string;
|
|
22
|
+
name?: string;
|
|
23
|
+
tool_call_id?: string;
|
|
24
|
+
tool_calls?: Array<{
|
|
25
|
+
id: string;
|
|
26
|
+
type: "function";
|
|
27
|
+
function: {
|
|
28
|
+
name: string;
|
|
29
|
+
arguments: string;
|
|
30
|
+
};
|
|
31
|
+
}>;
|
|
32
|
+
}
|
|
33
|
+
export interface ChatCompletionRequest {
|
|
34
|
+
model: DropstoneTier;
|
|
35
|
+
messages: ChatMessage[];
|
|
36
|
+
stream?: boolean;
|
|
37
|
+
temperature?: number;
|
|
38
|
+
top_p?: number;
|
|
39
|
+
max_tokens?: number;
|
|
40
|
+
stop?: string | string[];
|
|
41
|
+
tools?: any[];
|
|
42
|
+
tool_choice?: any;
|
|
43
|
+
/** Any additional OpenAI-compatible field; passed through verbatim. */
|
|
44
|
+
[k: string]: any;
|
|
45
|
+
}
|
|
46
|
+
export interface ChatCompletionUsage {
|
|
47
|
+
prompt_tokens: number;
|
|
48
|
+
completion_tokens: number;
|
|
49
|
+
total_tokens: number;
|
|
50
|
+
/** USD amount billed (real upstream cost × 1.3 markup). */
|
|
51
|
+
cost?: number;
|
|
52
|
+
}
|
|
53
|
+
export interface ChatCompletionResponse {
|
|
54
|
+
id: string;
|
|
55
|
+
object: "chat.completion";
|
|
56
|
+
created: number;
|
|
57
|
+
model: DropstoneTier;
|
|
58
|
+
choices: Array<{
|
|
59
|
+
index: number;
|
|
60
|
+
message: {
|
|
61
|
+
role: string;
|
|
62
|
+
content: string | null;
|
|
63
|
+
tool_calls?: any[];
|
|
64
|
+
};
|
|
65
|
+
finish_reason: string | null;
|
|
66
|
+
}>;
|
|
67
|
+
usage?: ChatCompletionUsage;
|
|
68
|
+
}
|
|
69
|
+
export interface ModelListResponse {
|
|
70
|
+
object: "list";
|
|
71
|
+
data: Array<{
|
|
72
|
+
id: DropstoneTier;
|
|
73
|
+
object: "model";
|
|
74
|
+
display_name: string;
|
|
75
|
+
owned_by: string;
|
|
76
|
+
}>;
|
|
77
|
+
}
|
|
78
|
+
export interface InsufficientCreditsError {
|
|
79
|
+
status: 402;
|
|
80
|
+
balance: number;
|
|
81
|
+
message: string;
|
|
82
|
+
topUpUrl: string;
|
|
83
|
+
}
|
|
84
|
+
export declare class DropstoneApiError extends Error {
|
|
85
|
+
status: number;
|
|
86
|
+
body: any;
|
|
87
|
+
constructor(status: number, body: any, message?: string);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Create a Dropstone HTTP API client. Reads `DROPSTONE_API_KEY` from env
|
|
91
|
+
* unless `apiKey` is passed explicitly.
|
|
92
|
+
*
|
|
93
|
+
* ```ts
|
|
94
|
+
* const dropstone = createDropstoneApi()
|
|
95
|
+
* const resp = await dropstone.chat.completions.create({
|
|
96
|
+
* model: "dropstone-fast",
|
|
97
|
+
* messages: [{ role: "user", content: "Hello" }],
|
|
98
|
+
* })
|
|
99
|
+
* console.log(resp.choices[0].message.content)
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
export declare function createDropstoneApi(options?: DropstoneApiOptions): {
|
|
103
|
+
chat: {
|
|
104
|
+
completions: {
|
|
105
|
+
create: {
|
|
106
|
+
(req: ChatCompletionRequest & {
|
|
107
|
+
stream?: false;
|
|
108
|
+
}): Promise<ChatCompletionResponse>;
|
|
109
|
+
(req: ChatCompletionRequest & {
|
|
110
|
+
stream: true;
|
|
111
|
+
}): Promise<AsyncIterable<any>>;
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
models: {
|
|
116
|
+
list: () => Promise<ModelListResponse>;
|
|
117
|
+
};
|
|
118
|
+
};
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// Headless API client for the Dropstone public HTTP API.
|
|
2
|
+
//
|
|
3
|
+
// This is the surface for CI, automation, serverless, and any other
|
|
4
|
+
// context where there is no interactive `dropstone` install. It does
|
|
5
|
+
// NOT spawn a local `dropstone serve` subprocess (that's what
|
|
6
|
+
// `createDropstone()` in index.ts does). It talks straight to
|
|
7
|
+
// https://api.dropstone.io/api/v1 with a `DROPSTONE_API_KEY` bearer.
|
|
8
|
+
//
|
|
9
|
+
// Surface mirrors the OpenAI SDK ergonomics so users can drop us in
|
|
10
|
+
// behind the OpenAI client if they prefer; we just give a slightly
|
|
11
|
+
// nicer wrapper with `usage.cost` typed.
|
|
12
|
+
const DEFAULT_BASE_URL = "https://api.dropstone.io/api/v1";
|
|
13
|
+
export class DropstoneApiError extends Error {
|
|
14
|
+
status;
|
|
15
|
+
body;
|
|
16
|
+
constructor(status, body, message) {
|
|
17
|
+
super(message ?? body?.error?.message ?? body?.message ?? `Dropstone API error ${status}`);
|
|
18
|
+
this.name = "DropstoneApiError";
|
|
19
|
+
this.status = status;
|
|
20
|
+
this.body = body;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Create a Dropstone HTTP API client. Reads `DROPSTONE_API_KEY` from env
|
|
25
|
+
* unless `apiKey` is passed explicitly.
|
|
26
|
+
*
|
|
27
|
+
* ```ts
|
|
28
|
+
* const dropstone = createDropstoneApi()
|
|
29
|
+
* const resp = await dropstone.chat.completions.create({
|
|
30
|
+
* model: "dropstone-fast",
|
|
31
|
+
* messages: [{ role: "user", content: "Hello" }],
|
|
32
|
+
* })
|
|
33
|
+
* console.log(resp.choices[0].message.content)
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export function createDropstoneApi(options = {}) {
|
|
37
|
+
const apiKey = options.apiKey ??
|
|
38
|
+
(typeof process !== "undefined" ? process.env.DROPSTONE_API_KEY : undefined);
|
|
39
|
+
if (!apiKey) {
|
|
40
|
+
throw new Error("DROPSTONE_API_KEY is not set. Pass `apiKey` to createDropstoneApi() " +
|
|
41
|
+
"or set the DROPSTONE_API_KEY environment variable. " +
|
|
42
|
+
"Generate a key at https://dropstone.io/dashboard/settings");
|
|
43
|
+
}
|
|
44
|
+
const baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
45
|
+
const fetchImpl = options.fetch ?? fetch;
|
|
46
|
+
// Shared headers builder — keeps Bearer + UA in one place.
|
|
47
|
+
const buildHeaders = (extra) => ({
|
|
48
|
+
Authorization: `Bearer ${apiKey}`,
|
|
49
|
+
"Content-Type": "application/json",
|
|
50
|
+
"User-Agent": "@blankline/dropstone-sdk",
|
|
51
|
+
...(extra ?? {}),
|
|
52
|
+
});
|
|
53
|
+
// Standardized error path so 402 (insufficient credits) is easy to
|
|
54
|
+
// detect — every consumer should special-case it to show a top-up CTA.
|
|
55
|
+
const parseError = async (res) => {
|
|
56
|
+
let body = null;
|
|
57
|
+
try {
|
|
58
|
+
body = await res.json();
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
try {
|
|
62
|
+
body = { error: { message: await res.text() } };
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
body = { error: { message: res.statusText } };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return new DropstoneApiError(res.status, body);
|
|
69
|
+
};
|
|
70
|
+
async function listModels() {
|
|
71
|
+
const res = await fetchImpl(`${baseUrl}/models`, { headers: buildHeaders() });
|
|
72
|
+
if (!res.ok)
|
|
73
|
+
throw await parseError(res);
|
|
74
|
+
return (await res.json());
|
|
75
|
+
}
|
|
76
|
+
async function createChatCompletion(req) {
|
|
77
|
+
const res = await fetchImpl(`${baseUrl}/chat/completions`, {
|
|
78
|
+
method: "POST",
|
|
79
|
+
headers: buildHeaders(),
|
|
80
|
+
body: JSON.stringify(req),
|
|
81
|
+
});
|
|
82
|
+
if (!res.ok)
|
|
83
|
+
throw await parseError(res);
|
|
84
|
+
// Non-streaming branch: just parse and return the OpenAI-shaped JSON.
|
|
85
|
+
if (!req.stream) {
|
|
86
|
+
return (await res.json());
|
|
87
|
+
}
|
|
88
|
+
// Streaming branch: yield each SSE `data: ` chunk parsed as JSON.
|
|
89
|
+
// Consumers can do `for await (const chunk of stream) { ... }`.
|
|
90
|
+
if (!res.body)
|
|
91
|
+
throw new DropstoneApiError(500, null, "Empty response body");
|
|
92
|
+
const reader = res.body.getReader();
|
|
93
|
+
const decoder = new TextDecoder();
|
|
94
|
+
return (async function* () {
|
|
95
|
+
let buf = "";
|
|
96
|
+
while (true) {
|
|
97
|
+
const { value, done } = await reader.read();
|
|
98
|
+
if (done)
|
|
99
|
+
break;
|
|
100
|
+
buf += decoder.decode(value, { stream: true });
|
|
101
|
+
const lines = buf.split("\n");
|
|
102
|
+
buf = lines.pop() ?? "";
|
|
103
|
+
for (const raw of lines) {
|
|
104
|
+
if (!raw.startsWith("data: "))
|
|
105
|
+
continue;
|
|
106
|
+
const payload = raw.slice(6).trim();
|
|
107
|
+
if (!payload || payload === "[DONE]")
|
|
108
|
+
continue;
|
|
109
|
+
try {
|
|
110
|
+
yield JSON.parse(payload);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// Ignore partial JSON — should not happen with proper SSE framing.
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
})();
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
chat: {
|
|
121
|
+
completions: { create: createChatCompletion },
|
|
122
|
+
},
|
|
123
|
+
models: { list: listModels },
|
|
124
|
+
};
|
|
125
|
+
}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from "./gen/types.gen.js";
|
|
2
|
+
import { type Config } from "./gen/client/types.gen.js";
|
|
3
|
+
import { DropstoneClient } from "./gen/sdk.gen.js";
|
|
4
|
+
export { type Config as DropstoneClientConfig, DropstoneClient };
|
|
5
|
+
export declare function createDropstoneClient(config?: Config & {
|
|
6
|
+
directory?: string;
|
|
7
|
+
}): DropstoneClient;
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export * from "./gen/types.gen.js";
|
|
2
|
+
import { createClient } from "./gen/client/client.gen.js";
|
|
3
|
+
import { DropstoneClient } from "./gen/sdk.gen.js";
|
|
4
|
+
export { DropstoneClient };
|
|
5
|
+
function pick(value, fallback) {
|
|
6
|
+
if (!value)
|
|
7
|
+
return;
|
|
8
|
+
if (!fallback)
|
|
9
|
+
return value;
|
|
10
|
+
if (value === fallback)
|
|
11
|
+
return fallback;
|
|
12
|
+
if (value === encodeURIComponent(fallback))
|
|
13
|
+
return fallback;
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
function rewrite(request, directory) {
|
|
17
|
+
if (request.method !== "GET" && request.method !== "HEAD")
|
|
18
|
+
return request;
|
|
19
|
+
const value = pick(request.headers.get("x-dropstone-directory"), directory);
|
|
20
|
+
if (!value)
|
|
21
|
+
return request;
|
|
22
|
+
const url = new URL(request.url);
|
|
23
|
+
if (!url.searchParams.has("directory")) {
|
|
24
|
+
url.searchParams.set("directory", value);
|
|
25
|
+
}
|
|
26
|
+
const next = new Request(url, request);
|
|
27
|
+
next.headers.delete("x-dropstone-directory");
|
|
28
|
+
return next;
|
|
29
|
+
}
|
|
30
|
+
export function createDropstoneClient(config) {
|
|
31
|
+
if (!config?.fetch) {
|
|
32
|
+
const customFetch = (req) => {
|
|
33
|
+
// @ts-ignore
|
|
34
|
+
req.timeout = false;
|
|
35
|
+
return fetch(req);
|
|
36
|
+
};
|
|
37
|
+
config = {
|
|
38
|
+
...config,
|
|
39
|
+
fetch: customFetch,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (config?.directory) {
|
|
43
|
+
config.headers = {
|
|
44
|
+
...config.headers,
|
|
45
|
+
"x-dropstone-directory": encodeURIComponent(config.directory),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const client = createClient(config);
|
|
49
|
+
client.interceptors.request.use((request) => rewrite(request, config?.directory));
|
|
50
|
+
return new DropstoneClient({ client });
|
|
51
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// This file is auto-generated by @hey-api/openapi-ts
|
|
2
|
+
import { createSseClient } from "../core/serverSentEvents.gen.js";
|
|
3
|
+
import { buildUrl, createConfig, createInterceptors, getParseAs, mergeConfigs, mergeHeaders, setAuthParams, } from "./utils.gen.js";
|
|
4
|
+
export const createClient = (config = {}) => {
|
|
5
|
+
let _config = mergeConfigs(createConfig(), config);
|
|
6
|
+
const getConfig = () => ({ ..._config });
|
|
7
|
+
const setConfig = (config) => {
|
|
8
|
+
_config = mergeConfigs(_config, config);
|
|
9
|
+
return getConfig();
|
|
10
|
+
};
|
|
11
|
+
const interceptors = createInterceptors();
|
|
12
|
+
const beforeRequest = async (options) => {
|
|
13
|
+
const opts = {
|
|
14
|
+
..._config,
|
|
15
|
+
...options,
|
|
16
|
+
fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
|
|
17
|
+
headers: mergeHeaders(_config.headers, options.headers),
|
|
18
|
+
serializedBody: undefined,
|
|
19
|
+
};
|
|
20
|
+
if (opts.security) {
|
|
21
|
+
await setAuthParams({
|
|
22
|
+
...opts,
|
|
23
|
+
security: opts.security,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
if (opts.requestValidator) {
|
|
27
|
+
await opts.requestValidator(opts);
|
|
28
|
+
}
|
|
29
|
+
if (opts.body && opts.bodySerializer) {
|
|
30
|
+
opts.serializedBody = opts.bodySerializer(opts.body);
|
|
31
|
+
}
|
|
32
|
+
// remove Content-Type header if body is empty to avoid sending invalid requests
|
|
33
|
+
if (opts.serializedBody === undefined || opts.serializedBody === "") {
|
|
34
|
+
opts.headers.delete("Content-Type");
|
|
35
|
+
}
|
|
36
|
+
const url = buildUrl(opts);
|
|
37
|
+
return { opts, url };
|
|
38
|
+
};
|
|
39
|
+
const request = async (options) => {
|
|
40
|
+
// @ts-expect-error
|
|
41
|
+
const { opts, url } = await beforeRequest(options);
|
|
42
|
+
const requestInit = {
|
|
43
|
+
redirect: "follow",
|
|
44
|
+
...opts,
|
|
45
|
+
body: opts.serializedBody,
|
|
46
|
+
};
|
|
47
|
+
let request = new Request(url, requestInit);
|
|
48
|
+
for (const fn of interceptors.request._fns) {
|
|
49
|
+
if (fn) {
|
|
50
|
+
request = await fn(request, opts);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// fetch must be assigned here, otherwise it would throw the error:
|
|
54
|
+
// TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation
|
|
55
|
+
const _fetch = opts.fetch;
|
|
56
|
+
let response = await _fetch(request);
|
|
57
|
+
for (const fn of interceptors.response._fns) {
|
|
58
|
+
if (fn) {
|
|
59
|
+
response = await fn(response, request, opts);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const result = {
|
|
63
|
+
request,
|
|
64
|
+
response,
|
|
65
|
+
};
|
|
66
|
+
if (response.ok) {
|
|
67
|
+
if (response.status === 204 || response.headers.get("Content-Length") === "0") {
|
|
68
|
+
return opts.responseStyle === "data"
|
|
69
|
+
? {}
|
|
70
|
+
: {
|
|
71
|
+
data: {},
|
|
72
|
+
...result,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const parseAs = (opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json";
|
|
76
|
+
let data;
|
|
77
|
+
switch (parseAs) {
|
|
78
|
+
case "arrayBuffer":
|
|
79
|
+
case "blob":
|
|
80
|
+
case "formData":
|
|
81
|
+
case "json":
|
|
82
|
+
case "text":
|
|
83
|
+
data = await response[parseAs]();
|
|
84
|
+
break;
|
|
85
|
+
case "stream":
|
|
86
|
+
return opts.responseStyle === "data"
|
|
87
|
+
? response.body
|
|
88
|
+
: {
|
|
89
|
+
data: response.body,
|
|
90
|
+
...result,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
if (parseAs === "json") {
|
|
94
|
+
if (opts.responseValidator) {
|
|
95
|
+
await opts.responseValidator(data);
|
|
96
|
+
}
|
|
97
|
+
if (opts.responseTransformer) {
|
|
98
|
+
data = await opts.responseTransformer(data);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return opts.responseStyle === "data"
|
|
102
|
+
? data
|
|
103
|
+
: {
|
|
104
|
+
data,
|
|
105
|
+
...result,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
const textError = await response.text();
|
|
109
|
+
let jsonError;
|
|
110
|
+
try {
|
|
111
|
+
jsonError = JSON.parse(textError);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// noop
|
|
115
|
+
}
|
|
116
|
+
const error = jsonError ?? textError;
|
|
117
|
+
let finalError = error;
|
|
118
|
+
for (const fn of interceptors.error._fns) {
|
|
119
|
+
if (fn) {
|
|
120
|
+
finalError = (await fn(error, response, request, opts));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
finalError = finalError || {};
|
|
124
|
+
if (opts.throwOnError) {
|
|
125
|
+
throw finalError;
|
|
126
|
+
}
|
|
127
|
+
// TODO: we probably want to return error and improve types
|
|
128
|
+
return opts.responseStyle === "data"
|
|
129
|
+
? undefined
|
|
130
|
+
: {
|
|
131
|
+
error: finalError,
|
|
132
|
+
...result,
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
const makeMethod = (method) => {
|
|
136
|
+
const fn = (options) => request({ ...options, method });
|
|
137
|
+
fn.sse = async (options) => {
|
|
138
|
+
const { opts, url } = await beforeRequest(options);
|
|
139
|
+
return createSseClient({
|
|
140
|
+
...opts,
|
|
141
|
+
body: opts.body,
|
|
142
|
+
headers: opts.headers,
|
|
143
|
+
method,
|
|
144
|
+
url,
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
return fn;
|
|
148
|
+
};
|
|
149
|
+
return {
|
|
150
|
+
buildUrl,
|
|
151
|
+
connect: makeMethod("CONNECT"),
|
|
152
|
+
delete: makeMethod("DELETE"),
|
|
153
|
+
get: makeMethod("GET"),
|
|
154
|
+
getConfig,
|
|
155
|
+
head: makeMethod("HEAD"),
|
|
156
|
+
interceptors,
|
|
157
|
+
options: makeMethod("OPTIONS"),
|
|
158
|
+
patch: makeMethod("PATCH"),
|
|
159
|
+
post: makeMethod("POST"),
|
|
160
|
+
put: makeMethod("PUT"),
|
|
161
|
+
request,
|
|
162
|
+
setConfig,
|
|
163
|
+
trace: makeMethod("TRACE"),
|
|
164
|
+
};
|
|
165
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type { Auth } from "../core/auth.gen.js";
|
|
2
|
+
export type { QuerySerializerOptions } from "../core/bodySerializer.gen.js";
|
|
3
|
+
export { formDataBodySerializer, jsonBodySerializer, urlSearchParamsBodySerializer, } from "../core/bodySerializer.gen.js";
|
|
4
|
+
export { buildClientParams } from "../core/params.gen.js";
|
|
5
|
+
export { createClient } from "./client.gen.js";
|
|
6
|
+
export type { Client, ClientOptions, Config, CreateClientConfig, Options, OptionsLegacyParser, RequestOptions, RequestResult, ResolvedRequestOptions, ResponseStyle, TDataShape, } from "./types.gen.js";
|
|
7
|
+
export { createConfig, mergeHeaders } from "./utils.gen.js";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// This file is auto-generated by @hey-api/openapi-ts
|
|
2
|
+
export { formDataBodySerializer, jsonBodySerializer, urlSearchParamsBodySerializer, } from "../core/bodySerializer.gen.js";
|
|
3
|
+
export { buildClientParams } from "../core/params.gen.js";
|
|
4
|
+
export { createClient } from "./client.gen.js";
|
|
5
|
+
export { createConfig, mergeHeaders } from "./utils.gen.js";
|