otterly 0.1.0 → 0.3.1
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 +109 -178
- package/dist/cli.js +7 -0
- package/dist/server/index.js +20 -0
- package/dist/server/playground.d.ts +1 -0
- package/dist/server/playground.js +1227 -0
- package/dist/server/routes-native.js +1 -1
- package/dist/server/swagger.d.ts +503 -0
- package/dist/server/swagger.js +320 -0
- package/package.json +2 -2
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
// OpenAPI 3.0 spec for the otterly local API server.
|
|
2
|
+
export const openApiSpec = {
|
|
3
|
+
openapi: "3.0.3",
|
|
4
|
+
info: {
|
|
5
|
+
title: "Otterly API",
|
|
6
|
+
version: "0.3.1",
|
|
7
|
+
description: "Local inference server with OpenAI-compatible and native endpoints. " +
|
|
8
|
+
"WebSocket available at ws://localhost:{port}/ws for interactive sessions.",
|
|
9
|
+
},
|
|
10
|
+
paths: {
|
|
11
|
+
"/api/status": {
|
|
12
|
+
get: {
|
|
13
|
+
summary: "Health check",
|
|
14
|
+
operationId: "getStatus",
|
|
15
|
+
responses: {
|
|
16
|
+
"200": {
|
|
17
|
+
description: "Server status",
|
|
18
|
+
content: {
|
|
19
|
+
"application/json": {
|
|
20
|
+
schema: {
|
|
21
|
+
type: "object",
|
|
22
|
+
properties: {
|
|
23
|
+
status: { type: "string", example: "ok" },
|
|
24
|
+
version: { type: "string", example: "0.3.1" },
|
|
25
|
+
activeSessions: { type: "integer" },
|
|
26
|
+
queue: {
|
|
27
|
+
type: "object",
|
|
28
|
+
properties: {
|
|
29
|
+
active: { type: "integer" },
|
|
30
|
+
queued: { type: "integer" },
|
|
31
|
+
maxConcurrent: { type: "integer" },
|
|
32
|
+
maxQueueSize: { type: "integer" },
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
circuitBreaker: { type: "string", enum: ["closed", "open", "half-open"] },
|
|
36
|
+
},
|
|
37
|
+
required: ["status", "version", "activeSessions"],
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
"/swagger.json": {
|
|
46
|
+
get: {
|
|
47
|
+
summary: "OpenAPI spec",
|
|
48
|
+
operationId: "getSwagger",
|
|
49
|
+
responses: {
|
|
50
|
+
"200": {
|
|
51
|
+
description: "This OpenAPI specification",
|
|
52
|
+
content: { "application/json": { schema: { type: "object" } } },
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
"/": {
|
|
58
|
+
get: {
|
|
59
|
+
summary: "Server info",
|
|
60
|
+
operationId: "getRoot",
|
|
61
|
+
responses: {
|
|
62
|
+
"200": {
|
|
63
|
+
description: "Server name, version, and playground link",
|
|
64
|
+
content: {
|
|
65
|
+
"application/json": {
|
|
66
|
+
schema: {
|
|
67
|
+
type: "object",
|
|
68
|
+
properties: {
|
|
69
|
+
name: { type: "string", example: "otterly" },
|
|
70
|
+
version: { type: "string", example: "0.3.1" },
|
|
71
|
+
playground: { type: "string", example: "/playground" },
|
|
72
|
+
},
|
|
73
|
+
required: ["name", "version", "playground"],
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
"/playground": {
|
|
82
|
+
get: {
|
|
83
|
+
summary: "Interactive API playground",
|
|
84
|
+
operationId: "getPlayground",
|
|
85
|
+
responses: {
|
|
86
|
+
"200": {
|
|
87
|
+
description: "HTML page with interactive API explorer",
|
|
88
|
+
content: { "text/html": { schema: { type: "string" } } },
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
"/v1/chat/completions": {
|
|
94
|
+
post: {
|
|
95
|
+
summary: "OpenAI-compatible chat completions",
|
|
96
|
+
operationId: "chatCompletions",
|
|
97
|
+
security: [{ BearerAuth: [] }],
|
|
98
|
+
parameters: [
|
|
99
|
+
{ name: "X-Session-Id", in: "header", schema: { type: "string" }, description: "Reuse an existing session" },
|
|
100
|
+
],
|
|
101
|
+
requestBody: {
|
|
102
|
+
required: true,
|
|
103
|
+
content: {
|
|
104
|
+
"application/json": {
|
|
105
|
+
schema: {
|
|
106
|
+
type: "object",
|
|
107
|
+
required: ["messages"],
|
|
108
|
+
properties: {
|
|
109
|
+
messages: {
|
|
110
|
+
type: "array",
|
|
111
|
+
items: {
|
|
112
|
+
type: "object",
|
|
113
|
+
required: ["role", "content"],
|
|
114
|
+
properties: {
|
|
115
|
+
role: { type: "string", enum: ["system", "user", "assistant"] },
|
|
116
|
+
content: { type: "string" },
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
model: { type: "string", default: "claude-sonnet-4-20250514" },
|
|
121
|
+
stream: { type: "boolean", default: false, description: "If true, response is SSE (text/event-stream)" },
|
|
122
|
+
tools: { type: "array", items: { type: "object" }, description: "OpenAI-format tool definitions" },
|
|
123
|
+
response_format: {
|
|
124
|
+
type: "object",
|
|
125
|
+
properties: { type: { type: "string", enum: ["text", "json_object"] } },
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
responses: {
|
|
133
|
+
"200": {
|
|
134
|
+
description: "Chat completion (or SSE stream when stream=true)",
|
|
135
|
+
content: {
|
|
136
|
+
"application/json": {
|
|
137
|
+
schema: { $ref: "#/components/schemas/ChatCompletionResponse" },
|
|
138
|
+
},
|
|
139
|
+
"text/event-stream": {
|
|
140
|
+
schema: { type: "string", description: "SSE stream of chat completion chunks" },
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
"400": { description: "Invalid request" },
|
|
145
|
+
"401": { description: "Unauthorized" },
|
|
146
|
+
"429": { description: "Rate limited or queue full" },
|
|
147
|
+
"503": { description: "Circuit breaker open" },
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
"/api/run": {
|
|
152
|
+
post: {
|
|
153
|
+
summary: "One-shot execution (native format)",
|
|
154
|
+
operationId: "apiRun",
|
|
155
|
+
security: [{ BearerAuth: [] }],
|
|
156
|
+
parameters: [
|
|
157
|
+
{ name: "X-Session-Id", in: "header", schema: { type: "string" }, description: "Reuse an existing session" },
|
|
158
|
+
],
|
|
159
|
+
requestBody: {
|
|
160
|
+
required: true,
|
|
161
|
+
content: {
|
|
162
|
+
"application/json": {
|
|
163
|
+
schema: {
|
|
164
|
+
type: "object",
|
|
165
|
+
required: ["prompt"],
|
|
166
|
+
properties: {
|
|
167
|
+
prompt: { type: "string" },
|
|
168
|
+
session_id: { type: "string" },
|
|
169
|
+
options: {
|
|
170
|
+
type: "object",
|
|
171
|
+
properties: {
|
|
172
|
+
cwd: { type: "string" },
|
|
173
|
+
permissionMode: { type: "string" },
|
|
174
|
+
systemPrompt: { type: "string" },
|
|
175
|
+
resume: { type: "string" },
|
|
176
|
+
model: { type: "string" },
|
|
177
|
+
maxTurns: { type: "integer" },
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
responses: {
|
|
186
|
+
"200": {
|
|
187
|
+
description: "Execution result",
|
|
188
|
+
content: {
|
|
189
|
+
"application/json": {
|
|
190
|
+
schema: { $ref: "#/components/schemas/NativeRunResponse" },
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
"400": { description: "Missing or invalid prompt" },
|
|
195
|
+
"401": { description: "Unauthorized" },
|
|
196
|
+
"429": { description: "Rate limited or queue full" },
|
|
197
|
+
"503": { description: "Circuit breaker open" },
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
"/api/stream": {
|
|
202
|
+
post: {
|
|
203
|
+
summary: "Streaming execution (NDJSON)",
|
|
204
|
+
operationId: "apiStream",
|
|
205
|
+
security: [{ BearerAuth: [] }],
|
|
206
|
+
parameters: [
|
|
207
|
+
{ name: "X-Session-Id", in: "header", schema: { type: "string" }, description: "Reuse an existing session" },
|
|
208
|
+
],
|
|
209
|
+
requestBody: {
|
|
210
|
+
required: true,
|
|
211
|
+
content: {
|
|
212
|
+
"application/json": {
|
|
213
|
+
schema: {
|
|
214
|
+
type: "object",
|
|
215
|
+
required: ["prompt"],
|
|
216
|
+
properties: {
|
|
217
|
+
prompt: { type: "string" },
|
|
218
|
+
session_id: { type: "string" },
|
|
219
|
+
options: {
|
|
220
|
+
type: "object",
|
|
221
|
+
properties: {
|
|
222
|
+
cwd: { type: "string" },
|
|
223
|
+
permissionMode: { type: "string" },
|
|
224
|
+
systemPrompt: { type: "string" },
|
|
225
|
+
resume: { type: "string" },
|
|
226
|
+
model: { type: "string" },
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
responses: {
|
|
235
|
+
"200": {
|
|
236
|
+
description: "NDJSON stream of events",
|
|
237
|
+
content: {
|
|
238
|
+
"application/x-ndjson": {
|
|
239
|
+
schema: { $ref: "#/components/schemas/StreamEvent" },
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
"400": { description: "Missing or invalid prompt" },
|
|
244
|
+
"401": { description: "Unauthorized" },
|
|
245
|
+
"429": { description: "Rate limited or queue full" },
|
|
246
|
+
"503": { description: "Circuit breaker open" },
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
components: {
|
|
252
|
+
securitySchemes: {
|
|
253
|
+
BearerAuth: {
|
|
254
|
+
type: "http",
|
|
255
|
+
scheme: "bearer",
|
|
256
|
+
description: "Set OTTERLY_API_KEY env var to enable. Pass as Bearer token.",
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
schemas: {
|
|
260
|
+
ChatCompletionResponse: {
|
|
261
|
+
type: "object",
|
|
262
|
+
properties: {
|
|
263
|
+
id: { type: "string" },
|
|
264
|
+
object: { type: "string", example: "chat.completion" },
|
|
265
|
+
created: { type: "integer" },
|
|
266
|
+
model: { type: "string" },
|
|
267
|
+
choices: {
|
|
268
|
+
type: "array",
|
|
269
|
+
items: {
|
|
270
|
+
type: "object",
|
|
271
|
+
properties: {
|
|
272
|
+
index: { type: "integer" },
|
|
273
|
+
message: {
|
|
274
|
+
type: "object",
|
|
275
|
+
properties: {
|
|
276
|
+
role: { type: "string" },
|
|
277
|
+
content: { type: "string" },
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
finish_reason: { type: "string" },
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
usage: {
|
|
285
|
+
type: "object",
|
|
286
|
+
properties: {
|
|
287
|
+
prompt_tokens: { type: "integer" },
|
|
288
|
+
completion_tokens: { type: "integer" },
|
|
289
|
+
total_tokens: { type: "integer" },
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
NativeRunResponse: {
|
|
295
|
+
type: "object",
|
|
296
|
+
properties: {
|
|
297
|
+
text: { type: "string" },
|
|
298
|
+
sessionId: { type: "string" },
|
|
299
|
+
cost: { type: "number" },
|
|
300
|
+
duration: { type: "number" },
|
|
301
|
+
usage: {
|
|
302
|
+
type: "object",
|
|
303
|
+
properties: {
|
|
304
|
+
inputTokens: { type: "integer" },
|
|
305
|
+
outputTokens: { type: "integer" },
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
StreamEvent: {
|
|
311
|
+
type: "object",
|
|
312
|
+
description: "One of: session_init, text_delta, tool_use, tool_result, result, error",
|
|
313
|
+
properties: {
|
|
314
|
+
type: { type: "string", enum: ["session_init", "text_delta", "tool_use", "tool_result", "result", "error"] },
|
|
315
|
+
},
|
|
316
|
+
required: ["type"],
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "otterly",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"description": "Local AI inference for your apps. Use Claude Code instead of paying for API tokens.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|