@curvet/sdk 0.1.0
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/LICENSE +21 -0
- package/README.md +160 -0
- package/dist/index.cjs +578 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +380 -0
- package/dist/index.d.ts +380 -0
- package/dist/index.js +532 -0
- package/dist/index.js.map +1 -0
- package/package.json +61 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
// src/core/errors.ts
|
|
2
|
+
var CurvetError = class extends Error {
|
|
3
|
+
constructor(message, opts = {}) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = new.target.name;
|
|
6
|
+
this.status = opts.status;
|
|
7
|
+
this.requestId = opts.requestId;
|
|
8
|
+
this.raw = opts.raw;
|
|
9
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var AuthError = class extends CurvetError {
|
|
13
|
+
};
|
|
14
|
+
var PermissionError = class extends CurvetError {
|
|
15
|
+
};
|
|
16
|
+
var BadRequestError = class extends CurvetError {
|
|
17
|
+
};
|
|
18
|
+
var NotFoundError = class extends CurvetError {
|
|
19
|
+
};
|
|
20
|
+
var APIError = class extends CurvetError {
|
|
21
|
+
};
|
|
22
|
+
var ConnectionError = class extends CurvetError {
|
|
23
|
+
};
|
|
24
|
+
var InsufficientBalanceError = class extends CurvetError {
|
|
25
|
+
};
|
|
26
|
+
var RateLimitError = class extends CurvetError {
|
|
27
|
+
};
|
|
28
|
+
var JobFailedError = class extends CurvetError {
|
|
29
|
+
constructor(message, jobId, opts = {}) {
|
|
30
|
+
super(message, opts);
|
|
31
|
+
this.jobId = jobId;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
var JobTimeoutError = class extends CurvetError {
|
|
35
|
+
constructor(message, jobId, opts = {}) {
|
|
36
|
+
super(message, opts);
|
|
37
|
+
this.jobId = jobId;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
function errorFromResponse(status, body, requestId, headers) {
|
|
41
|
+
const b = body ?? {};
|
|
42
|
+
const message = typeof b.error === "string" ? b.error : `HTTP ${status}`;
|
|
43
|
+
const base = { status, requestId, raw: body };
|
|
44
|
+
switch (status) {
|
|
45
|
+
case 400:
|
|
46
|
+
return new BadRequestError(message, base);
|
|
47
|
+
case 401:
|
|
48
|
+
return new AuthError(message, base);
|
|
49
|
+
case 402: {
|
|
50
|
+
const e = new InsufficientBalanceError(message, base);
|
|
51
|
+
e.required = b.required;
|
|
52
|
+
e.available = b.available;
|
|
53
|
+
return e;
|
|
54
|
+
}
|
|
55
|
+
case 403:
|
|
56
|
+
return new PermissionError(message, base);
|
|
57
|
+
case 404:
|
|
58
|
+
return new NotFoundError(message, base);
|
|
59
|
+
case 429: {
|
|
60
|
+
const e = new RateLimitError(message, base);
|
|
61
|
+
const info = b.rateLimitInfo ?? b.costCapInfo;
|
|
62
|
+
e.kind = b.costCapInfo ? "cost" : "rate";
|
|
63
|
+
if (info) {
|
|
64
|
+
e.limit = info.limit;
|
|
65
|
+
e.used = info.used;
|
|
66
|
+
if (info.resetsAt) e.resetsAt = new Date(info.resetsAt);
|
|
67
|
+
}
|
|
68
|
+
const retryAfter = headers?.get?.("retry-after");
|
|
69
|
+
if (info?.resetsIn != null) e.retryAfterMs = Number(info.resetsIn) * 1e3;
|
|
70
|
+
else if (retryAfter) e.retryAfterMs = Number(retryAfter) * 1e3;
|
|
71
|
+
else if (e.resetsAt) e.retryAfterMs = Math.max(0, e.resetsAt.getTime() - Date.now());
|
|
72
|
+
return e;
|
|
73
|
+
}
|
|
74
|
+
default:
|
|
75
|
+
return new APIError(message, base);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/core/retry.ts
|
|
80
|
+
function fullJitterBackoff(attempt, baseMs = 500, capMs = 8e3) {
|
|
81
|
+
const exp = Math.min(capMs, baseMs * 2 ** attempt);
|
|
82
|
+
return Math.random() * exp;
|
|
83
|
+
}
|
|
84
|
+
function sleep(ms, signal) {
|
|
85
|
+
return new Promise((resolve, reject) => {
|
|
86
|
+
if (signal?.aborted) return reject(abortError());
|
|
87
|
+
const onAbort = () => {
|
|
88
|
+
cleanup();
|
|
89
|
+
reject(abortError());
|
|
90
|
+
};
|
|
91
|
+
const cleanup = () => {
|
|
92
|
+
clearTimeout(timer);
|
|
93
|
+
signal?.removeEventListener("abort", onAbort);
|
|
94
|
+
};
|
|
95
|
+
const timer = setTimeout(() => {
|
|
96
|
+
cleanup();
|
|
97
|
+
resolve();
|
|
98
|
+
}, ms);
|
|
99
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
function abortError() {
|
|
103
|
+
const e = new Error("The operation was aborted.");
|
|
104
|
+
e.name = "AbortError";
|
|
105
|
+
return e;
|
|
106
|
+
}
|
|
107
|
+
function isRetryableStatus(status) {
|
|
108
|
+
return status === 429 || status >= 500;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/core/http.ts
|
|
112
|
+
var HttpClient = class {
|
|
113
|
+
constructor(opts) {
|
|
114
|
+
this.opts = opts;
|
|
115
|
+
}
|
|
116
|
+
async request(args) {
|
|
117
|
+
const { method, path, body, query, options } = args;
|
|
118
|
+
const url = this.buildUrl(path, query);
|
|
119
|
+
const maxRetries = options?.maxRetries ?? this.opts.maxRetries;
|
|
120
|
+
const timeout = options?.timeout ?? this.opts.timeout;
|
|
121
|
+
const headers = {
|
|
122
|
+
"x-app-key": this.opts.appKey,
|
|
123
|
+
accept: "application/json",
|
|
124
|
+
...options?.headers ?? {}
|
|
125
|
+
};
|
|
126
|
+
let payload;
|
|
127
|
+
if (body !== void 0) {
|
|
128
|
+
headers["content-type"] = "application/json";
|
|
129
|
+
payload = JSON.stringify(body);
|
|
130
|
+
}
|
|
131
|
+
let attempt = 0;
|
|
132
|
+
for (; ; ) {
|
|
133
|
+
const { signal, done } = this.makeSignal(timeout, options?.signal);
|
|
134
|
+
try {
|
|
135
|
+
const res = await this.opts.fetch(url, { method, headers, body: payload, signal });
|
|
136
|
+
const text = await res.text();
|
|
137
|
+
const parsed = text ? safeJson(text) : void 0;
|
|
138
|
+
if (res.ok) return parsed;
|
|
139
|
+
const requestId = res.headers.get("x-request-id") ?? parsed?.metadata?.requestId ?? void 0;
|
|
140
|
+
const err = errorFromResponse(res.status, parsed, requestId, res.headers);
|
|
141
|
+
if (isRetryableStatus(res.status) && attempt < maxRetries) {
|
|
142
|
+
await this.backoff(err, attempt, options?.signal);
|
|
143
|
+
attempt++;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
throw err;
|
|
147
|
+
} catch (e) {
|
|
148
|
+
if (e instanceof CurvetError) throw e;
|
|
149
|
+
if (e?.name === "AbortError") {
|
|
150
|
+
if (options?.signal?.aborted) throw e;
|
|
151
|
+
if (attempt < maxRetries) {
|
|
152
|
+
await this.backoff(void 0, attempt, options?.signal);
|
|
153
|
+
attempt++;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
throw new ConnectionError("Request timed out", { raw: e });
|
|
157
|
+
}
|
|
158
|
+
if (attempt < maxRetries) {
|
|
159
|
+
await this.backoff(void 0, attempt, options?.signal);
|
|
160
|
+
attempt++;
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
throw new ConnectionError(e?.message ?? "Network request failed", { raw: e });
|
|
164
|
+
} finally {
|
|
165
|
+
done();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
async backoff(err, attempt, signal) {
|
|
170
|
+
let delay = fullJitterBackoff(attempt);
|
|
171
|
+
if (err instanceof RateLimitError && err.retryAfterMs != null) {
|
|
172
|
+
delay = Math.min(err.retryAfterMs, 3e4);
|
|
173
|
+
}
|
|
174
|
+
await sleep(delay, signal);
|
|
175
|
+
}
|
|
176
|
+
buildUrl(path, query) {
|
|
177
|
+
const base = this.opts.baseURL.replace(/\/$/, "");
|
|
178
|
+
let url = base + (path.startsWith("/") ? path : "/" + path);
|
|
179
|
+
if (query) {
|
|
180
|
+
const qs = Object.entries(query).filter(([, v]) => v !== void 0).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`).join("&");
|
|
181
|
+
if (qs) url += "?" + qs;
|
|
182
|
+
}
|
|
183
|
+
return url;
|
|
184
|
+
}
|
|
185
|
+
/** Combine a per-request timeout with an optional user AbortSignal. */
|
|
186
|
+
makeSignal(timeout, userSignal) {
|
|
187
|
+
const controller = new AbortController();
|
|
188
|
+
const onUserAbort = () => controller.abort();
|
|
189
|
+
if (userSignal) {
|
|
190
|
+
if (userSignal.aborted) controller.abort();
|
|
191
|
+
else userSignal.addEventListener("abort", onUserAbort, { once: true });
|
|
192
|
+
}
|
|
193
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
194
|
+
return {
|
|
195
|
+
signal: controller.signal,
|
|
196
|
+
done: () => {
|
|
197
|
+
clearTimeout(timer);
|
|
198
|
+
userSignal?.removeEventListener("abort", onUserAbort);
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
function safeJson(text) {
|
|
204
|
+
try {
|
|
205
|
+
return JSON.parse(text);
|
|
206
|
+
} catch {
|
|
207
|
+
return text;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// src/resources/chat.ts
|
|
212
|
+
var Chat = class {
|
|
213
|
+
constructor(client) {
|
|
214
|
+
this.client = client;
|
|
215
|
+
}
|
|
216
|
+
/** Create a chat completion. Synchronous — resolves with the model's reply. */
|
|
217
|
+
create(params, options) {
|
|
218
|
+
return this.client.request({
|
|
219
|
+
method: "POST",
|
|
220
|
+
path: "/chat",
|
|
221
|
+
body: params,
|
|
222
|
+
options
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// src/resources/image.ts
|
|
228
|
+
var Images = class {
|
|
229
|
+
constructor(client) {
|
|
230
|
+
this.client = client;
|
|
231
|
+
}
|
|
232
|
+
/** Generate an image from a prompt. Synchronous — resolves with `imageUrl`. */
|
|
233
|
+
generate(params, options) {
|
|
234
|
+
return this.client.request({
|
|
235
|
+
method: "POST",
|
|
236
|
+
path: "/image",
|
|
237
|
+
body: params,
|
|
238
|
+
options
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
// src/core/normalize.ts
|
|
244
|
+
function pickMediaUrl(body) {
|
|
245
|
+
return body?.videoUrl ?? body?.audioUrl ?? body?.modelUrl ?? body?.output?.mediaUrl ?? body?.mediaUrl ?? void 0;
|
|
246
|
+
}
|
|
247
|
+
function normalizeMediaPost(body) {
|
|
248
|
+
const b = body ?? {};
|
|
249
|
+
const mediaUrl = pickMediaUrl(b);
|
|
250
|
+
const jobId = b.jobId ?? b.metadata?.jobId;
|
|
251
|
+
const status = mediaUrl ? "completed" : b.status === "failed" ? "failed" : "processing";
|
|
252
|
+
return {
|
|
253
|
+
jobId,
|
|
254
|
+
status,
|
|
255
|
+
mediaUrl,
|
|
256
|
+
usage: b.usage,
|
|
257
|
+
metadata: b.metadata,
|
|
258
|
+
error: b.error ?? null,
|
|
259
|
+
raw: body
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
function normalizeJob(body) {
|
|
263
|
+
const b = body ?? {};
|
|
264
|
+
const mediaUrl = pickMediaUrl(b);
|
|
265
|
+
const status = b.status ?? (mediaUrl ? "completed" : "processing");
|
|
266
|
+
return {
|
|
267
|
+
jobId: b.jobId,
|
|
268
|
+
status,
|
|
269
|
+
progress: b.progress,
|
|
270
|
+
mediaUrl,
|
|
271
|
+
metadata: b.output?.metadata ?? b.metadata,
|
|
272
|
+
error: b.error ?? null,
|
|
273
|
+
cost: b.cost,
|
|
274
|
+
eta: b.eta,
|
|
275
|
+
raw: body
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// src/core/poll.ts
|
|
280
|
+
var PollTimeoutError = class extends Error {
|
|
281
|
+
constructor(message = "Polling timed out") {
|
|
282
|
+
super(message);
|
|
283
|
+
this.name = "PollTimeoutError";
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
async function pollUntil(fn, cfg) {
|
|
287
|
+
const start = Date.now();
|
|
288
|
+
for (; ; ) {
|
|
289
|
+
const result = await fn();
|
|
290
|
+
cfg.onTick?.(result);
|
|
291
|
+
if (cfg.isTerminal(result)) return result;
|
|
292
|
+
const elapsed = Date.now() - start;
|
|
293
|
+
if (elapsed >= cfg.timeoutMs) throw new PollTimeoutError();
|
|
294
|
+
const remaining = cfg.timeoutMs - elapsed;
|
|
295
|
+
await sleep(Math.min(cfg.intervalMs, Math.max(0, remaining)), cfg.signal);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// src/resources/jobs.ts
|
|
300
|
+
var Jobs = class {
|
|
301
|
+
constructor(client, defaults) {
|
|
302
|
+
this.client = client;
|
|
303
|
+
this.defaults = defaults;
|
|
304
|
+
}
|
|
305
|
+
/** Fetch the current status of an async job once (no polling). */
|
|
306
|
+
async retrieve(jobId, options) {
|
|
307
|
+
const body = await this.client.request({
|
|
308
|
+
method: "GET",
|
|
309
|
+
path: `/jobs/${encodeURIComponent(jobId)}`,
|
|
310
|
+
options
|
|
311
|
+
});
|
|
312
|
+
return normalizeJob(body);
|
|
313
|
+
}
|
|
314
|
+
/** Get a {@link Job} handle to poll/await an existing job by id. */
|
|
315
|
+
handle(jobId) {
|
|
316
|
+
return new Job(jobId, this.client, this.defaults);
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
var Job = class {
|
|
320
|
+
constructor(id, client, defaults) {
|
|
321
|
+
this.id = id;
|
|
322
|
+
this.client = client;
|
|
323
|
+
this.defaults = defaults;
|
|
324
|
+
}
|
|
325
|
+
/** One status fetch (no polling). */
|
|
326
|
+
retrieve(options) {
|
|
327
|
+
return new Jobs(this.client, this.defaults).retrieve(this.id, options);
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Poll until the job reaches a terminal state.
|
|
331
|
+
* Resolves with the completed job, or throws JobFailedError / JobTimeoutError.
|
|
332
|
+
*/
|
|
333
|
+
async wait(opts = {}) {
|
|
334
|
+
const intervalMs = opts.pollIntervalMs ?? this.defaults.pollIntervalMs;
|
|
335
|
+
const timeoutMs = opts.pollTimeoutMs ?? this.defaults.pollTimeoutMs;
|
|
336
|
+
let result;
|
|
337
|
+
try {
|
|
338
|
+
result = await pollUntil(() => this.retrieve({ signal: opts.signal }), {
|
|
339
|
+
intervalMs,
|
|
340
|
+
timeoutMs,
|
|
341
|
+
signal: opts.signal,
|
|
342
|
+
isTerminal: (r) => r.status === "completed" || r.status === "failed",
|
|
343
|
+
onTick: (r) => opts.onProgress?.(r.progress ?? 0, r.eta)
|
|
344
|
+
});
|
|
345
|
+
} catch (e) {
|
|
346
|
+
if (e instanceof PollTimeoutError) {
|
|
347
|
+
throw new JobTimeoutError(
|
|
348
|
+
`Job ${this.id} did not complete within ${timeoutMs}ms`,
|
|
349
|
+
this.id
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
throw e;
|
|
353
|
+
}
|
|
354
|
+
if (result.status === "failed") {
|
|
355
|
+
throw new JobFailedError(result.error || `Job ${this.id} failed`, this.id, {
|
|
356
|
+
raw: result.raw
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
return result;
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
// src/resources/video.ts
|
|
364
|
+
var Video = class {
|
|
365
|
+
constructor(client, defaults, path = "/video") {
|
|
366
|
+
this.client = client;
|
|
367
|
+
this.defaults = defaults;
|
|
368
|
+
this.path = path;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Submit a job WITHOUT polling. Returns once the server responds — either the
|
|
372
|
+
* 200 fast-path (already done) or a 202 with a jobId.
|
|
373
|
+
*
|
|
374
|
+
* The media POST long-polls server-side and can block well past a normal
|
|
375
|
+
* request timeout, so we default its timeout to the poll budget and disable
|
|
376
|
+
* auto-retry (a retried POST would enqueue a duplicate, double-charged job).
|
|
377
|
+
*/
|
|
378
|
+
async submit(params, options) {
|
|
379
|
+
const reqOptions = {
|
|
380
|
+
...options,
|
|
381
|
+
timeout: options?.timeout ?? this.defaults.pollTimeoutMs,
|
|
382
|
+
maxRetries: options?.maxRetries ?? 0
|
|
383
|
+
};
|
|
384
|
+
const body = await this.client.request({
|
|
385
|
+
method: "POST",
|
|
386
|
+
path: this.path,
|
|
387
|
+
body: params,
|
|
388
|
+
options: reqOptions
|
|
389
|
+
});
|
|
390
|
+
return normalizeMediaPost(body);
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Submit and resolve to the finished media. Handles the 200-vs-202 split and
|
|
394
|
+
* polls `/jobs/:id` internally. Throws JobFailedError / JobTimeoutError.
|
|
395
|
+
*/
|
|
396
|
+
async generate(params, options) {
|
|
397
|
+
const submitted = await this.submit(params, options);
|
|
398
|
+
if (submitted.status === "completed" || submitted.status === "failed") {
|
|
399
|
+
return submitted;
|
|
400
|
+
}
|
|
401
|
+
if (!submitted.jobId) {
|
|
402
|
+
throw new CurvetError("Async job did not return a jobId to poll", {
|
|
403
|
+
raw: submitted.raw
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
const job = new Job(submitted.jobId, this.client, this.defaults);
|
|
407
|
+
return job.wait({
|
|
408
|
+
pollIntervalMs: options?.pollIntervalMs,
|
|
409
|
+
pollTimeoutMs: options?.pollTimeoutMs,
|
|
410
|
+
signal: options?.signal,
|
|
411
|
+
onProgress: options?.onProgress
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
// src/resources/models.ts
|
|
417
|
+
var Models = class {
|
|
418
|
+
constructor(client, cacheTtlMs = 6e4) {
|
|
419
|
+
this.client = client;
|
|
420
|
+
this.cacheTtlMs = cacheTtlMs;
|
|
421
|
+
}
|
|
422
|
+
async load(options) {
|
|
423
|
+
return this.client.request({
|
|
424
|
+
method: "GET",
|
|
425
|
+
path: "/models",
|
|
426
|
+
options
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
async ensure(options) {
|
|
430
|
+
const stale = !this.cache || Date.now() - this.cache.at > this.cacheTtlMs;
|
|
431
|
+
if (options?.refresh || stale) {
|
|
432
|
+
this.cache = { at: Date.now(), data: await this.load(options) };
|
|
433
|
+
}
|
|
434
|
+
return this.cache.data;
|
|
435
|
+
}
|
|
436
|
+
/** List available models, optionally filtered by `type`. */
|
|
437
|
+
async list(options) {
|
|
438
|
+
const data = await this.ensure(options);
|
|
439
|
+
const models = data.models ?? [];
|
|
440
|
+
return options?.type ? models.filter((m) => m.type === options.type) : models;
|
|
441
|
+
}
|
|
442
|
+
/** Find a single model by id (or undefined). */
|
|
443
|
+
async get(id, options) {
|
|
444
|
+
return (await this.list(options)).find((m) => m.id === id);
|
|
445
|
+
}
|
|
446
|
+
/** The app's rate limits as reported by GET /models. */
|
|
447
|
+
async rateLimits(options) {
|
|
448
|
+
return (await this.ensure(options)).rateLimits;
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
// src/resources/balance.ts
|
|
453
|
+
var Balance = class {
|
|
454
|
+
constructor(client) {
|
|
455
|
+
this.client = client;
|
|
456
|
+
}
|
|
457
|
+
/** Get the current credit balance for the app owner. */
|
|
458
|
+
async get(options) {
|
|
459
|
+
const body = await this.client.request({
|
|
460
|
+
method: "GET",
|
|
461
|
+
path: "/balance",
|
|
462
|
+
options
|
|
463
|
+
});
|
|
464
|
+
return body.balance;
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
// src/client.ts
|
|
469
|
+
var DEFAULT_BASE_URL = "https://curvet.ai/api/v1/playground";
|
|
470
|
+
var Curvet = class {
|
|
471
|
+
constructor(options = {}) {
|
|
472
|
+
const appKey = options.appKey ?? envKey();
|
|
473
|
+
if (!appKey) {
|
|
474
|
+
throw new CurvetError(
|
|
475
|
+
"Missing Curvet app key. Pass { appKey } or set the CURVET_APP_KEY environment variable."
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
const fetchImpl = options.fetch ?? defaultFetch();
|
|
479
|
+
if (!fetchImpl) {
|
|
480
|
+
throw new CurvetError(
|
|
481
|
+
"No fetch implementation available. Use Node 18+ or pass { fetch }."
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
const client = new HttpClient({
|
|
485
|
+
appKey,
|
|
486
|
+
baseURL: options.baseURL ?? DEFAULT_BASE_URL,
|
|
487
|
+
timeout: options.timeout ?? 6e4,
|
|
488
|
+
maxRetries: options.maxRetries ?? 2,
|
|
489
|
+
fetch: fetchImpl
|
|
490
|
+
});
|
|
491
|
+
const jobDefaults = {
|
|
492
|
+
pollIntervalMs: options.defaultPollIntervalMs ?? 2500,
|
|
493
|
+
pollTimeoutMs: options.defaultPollTimeoutMs ?? 18e4
|
|
494
|
+
};
|
|
495
|
+
this.chat = new Chat(client);
|
|
496
|
+
this.image = new Images(client);
|
|
497
|
+
this.jobs = new Jobs(client, jobDefaults);
|
|
498
|
+
this.video = new Video(client, jobDefaults);
|
|
499
|
+
this.models = new Models(client);
|
|
500
|
+
this.balance = new Balance(client);
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
function envKey() {
|
|
504
|
+
return typeof process !== "undefined" ? process.env?.CURVET_APP_KEY : void 0;
|
|
505
|
+
}
|
|
506
|
+
function defaultFetch() {
|
|
507
|
+
const f = globalThis.fetch;
|
|
508
|
+
return typeof f === "function" ? f.bind(globalThis) : void 0;
|
|
509
|
+
}
|
|
510
|
+
export {
|
|
511
|
+
APIError,
|
|
512
|
+
AuthError,
|
|
513
|
+
BadRequestError,
|
|
514
|
+
Balance,
|
|
515
|
+
Chat,
|
|
516
|
+
ConnectionError,
|
|
517
|
+
Curvet,
|
|
518
|
+
CurvetError,
|
|
519
|
+
DEFAULT_BASE_URL,
|
|
520
|
+
Images,
|
|
521
|
+
InsufficientBalanceError,
|
|
522
|
+
Job,
|
|
523
|
+
JobFailedError,
|
|
524
|
+
JobTimeoutError,
|
|
525
|
+
Jobs,
|
|
526
|
+
Models,
|
|
527
|
+
NotFoundError,
|
|
528
|
+
PermissionError,
|
|
529
|
+
RateLimitError,
|
|
530
|
+
Video
|
|
531
|
+
};
|
|
532
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/errors.ts","../src/core/retry.ts","../src/core/http.ts","../src/resources/chat.ts","../src/resources/image.ts","../src/core/normalize.ts","../src/core/poll.ts","../src/resources/jobs.ts","../src/resources/video.ts","../src/resources/models.ts","../src/resources/balance.ts","../src/client.ts"],"sourcesContent":["export interface CurvetErrorOptions {\n status?: number;\n requestId?: string;\n raw?: unknown;\n}\n\n/** Base class for every error thrown by the SDK. */\nexport class CurvetError extends Error {\n readonly status?: number;\n readonly requestId?: string;\n readonly raw?: unknown;\n\n constructor(message: string, opts: CurvetErrorOptions = {}) {\n super(message);\n this.name = new.target.name;\n this.status = opts.status;\n this.requestId = opts.requestId;\n this.raw = opts.raw;\n // Restore prototype chain so `instanceof` works after transpilation.\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** 401 — missing or invalid `x-app-key`. */\nexport class AuthError extends CurvetError {}\n/** 403 — app not active, playground not enabled, or model/category not allowed. */\nexport class PermissionError extends CurvetError {}\n/** 400 — invalid/unknown model or malformed payload. */\nexport class BadRequestError extends CurvetError {}\n/** 404 — job or workflow not found. */\nexport class NotFoundError extends CurvetError {}\n/** 5xx — upstream/server error (retried automatically). */\nexport class APIError extends CurvetError {}\n/** Network failure or timeout before a response was received. */\nexport class ConnectionError extends CurvetError {}\n\n/** 402 — not enough credits to complete the request. */\nexport class InsufficientBalanceError extends CurvetError {\n required?: number;\n available?: number;\n}\n\n/** 429 — hourly request limit or daily cost cap exceeded. */\nexport class RateLimitError extends CurvetError {\n kind?: \"rate\" | \"cost\";\n limit?: number;\n used?: number;\n resetsAt?: Date;\n retryAfterMs?: number;\n}\n\n/** An async media job finished with status \"failed\". */\nexport class JobFailedError extends CurvetError {\n readonly jobId: string;\n constructor(message: string, jobId: string, opts: CurvetErrorOptions = {}) {\n super(message, opts);\n this.jobId = jobId;\n }\n}\n\n/** An async media job did not finish within the poll timeout. */\nexport class JobTimeoutError extends CurvetError {\n readonly jobId: string;\n constructor(message: string, jobId: string, opts: CurvetErrorOptions = {}) {\n super(message, opts);\n this.jobId = jobId;\n }\n}\n\ninterface HeaderBag {\n get(name: string): string | null;\n}\n\n/** Map an HTTP status + JSON error body to the right typed error. */\nexport function errorFromResponse(\n status: number,\n body: unknown,\n requestId?: string,\n headers?: HeaderBag,\n): CurvetError {\n const b = (body ?? {}) as Record<string, any>;\n const message =\n typeof b.error === \"string\" ? b.error : `HTTP ${status}`;\n const base: CurvetErrorOptions = { status, requestId, raw: body };\n\n switch (status) {\n case 400:\n return new BadRequestError(message, base);\n case 401:\n return new AuthError(message, base);\n case 402: {\n const e = new InsufficientBalanceError(message, base);\n e.required = b.required;\n e.available = b.available;\n return e;\n }\n case 403:\n return new PermissionError(message, base);\n case 404:\n return new NotFoundError(message, base);\n case 429: {\n const e = new RateLimitError(message, base);\n const info = b.rateLimitInfo ?? b.costCapInfo;\n e.kind = b.costCapInfo ? \"cost\" : \"rate\";\n if (info) {\n e.limit = info.limit;\n e.used = info.used;\n if (info.resetsAt) e.resetsAt = new Date(info.resetsAt);\n }\n const retryAfter = headers?.get?.(\"retry-after\");\n if (info?.resetsIn != null) e.retryAfterMs = Number(info.resetsIn) * 1000;\n else if (retryAfter) e.retryAfterMs = Number(retryAfter) * 1000;\n else if (e.resetsAt) e.retryAfterMs = Math.max(0, e.resetsAt.getTime() - Date.now());\n return e;\n }\n default:\n return new APIError(message, base);\n }\n}\n","/** Exponential backoff with full jitter. */\nexport function fullJitterBackoff(attempt: number, baseMs = 500, capMs = 8000): number {\n const exp = Math.min(capMs, baseMs * 2 ** attempt);\n return Math.random() * exp;\n}\n\n/** Sleep that rejects with an AbortError if the signal fires. */\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) return reject(abortError());\n const onAbort = () => {\n cleanup();\n reject(abortError());\n };\n const cleanup = () => {\n clearTimeout(timer);\n signal?.removeEventListener(\"abort\", onAbort);\n };\n const timer = setTimeout(() => {\n cleanup();\n resolve();\n }, ms);\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\nexport function abortError(): Error {\n const e = new Error(\"The operation was aborted.\");\n e.name = \"AbortError\";\n return e;\n}\n\n/** Only 429 and 5xx are safe to retry. */\nexport function isRetryableStatus(status: number): boolean {\n return status === 429 || status >= 500;\n}\n","import type { FetchLike, RequestOptions } from \"../types/common\";\nimport {\n CurvetError,\n ConnectionError,\n RateLimitError,\n errorFromResponse,\n} from \"./errors\";\nimport { fullJitterBackoff, isRetryableStatus, sleep } from \"./retry\";\n\nexport interface HttpClientOptions {\n appKey: string;\n baseURL: string;\n timeout: number;\n maxRetries: number;\n fetch: FetchLike;\n}\n\nexport interface RequestArgs {\n method: string;\n path: string;\n body?: unknown;\n query?: Record<string, string | number | undefined>;\n options?: RequestOptions;\n}\n\n/**\n * The single place that knows about fetch, headers, base URL, error mapping,\n * and retries. Resources call `request()`; everything else stays mockable.\n */\nexport class HttpClient {\n constructor(private opts: HttpClientOptions) {}\n\n async request<T = any>(args: RequestArgs): Promise<T> {\n const { method, path, body, query, options } = args;\n const url = this.buildUrl(path, query);\n const maxRetries = options?.maxRetries ?? this.opts.maxRetries;\n const timeout = options?.timeout ?? this.opts.timeout;\n\n const headers: Record<string, string> = {\n \"x-app-key\": this.opts.appKey,\n accept: \"application/json\",\n ...(options?.headers ?? {}),\n };\n let payload: string | undefined;\n if (body !== undefined) {\n headers[\"content-type\"] = \"application/json\";\n payload = JSON.stringify(body);\n }\n\n let attempt = 0;\n for (;;) {\n const { signal, done } = this.makeSignal(timeout, options?.signal);\n try {\n const res = await this.opts.fetch(url, { method, headers, body: payload, signal });\n const text = await res.text();\n const parsed = text ? safeJson(text) : undefined;\n\n if (res.ok) return parsed as T;\n\n const requestId =\n res.headers.get(\"x-request-id\") ??\n (parsed as any)?.metadata?.requestId ??\n undefined;\n const err = errorFromResponse(res.status, parsed, requestId, res.headers);\n\n if (isRetryableStatus(res.status) && attempt < maxRetries) {\n await this.backoff(err, attempt, options?.signal);\n attempt++;\n continue;\n }\n throw err;\n } catch (e: any) {\n if (e instanceof CurvetError) throw e;\n // AbortError: distinguish user-abort (rethrow) from our timeout (retry/ConnectionError).\n if (e?.name === \"AbortError\") {\n if (options?.signal?.aborted) throw e;\n if (attempt < maxRetries) {\n await this.backoff(undefined, attempt, options?.signal);\n attempt++;\n continue;\n }\n throw new ConnectionError(\"Request timed out\", { raw: e });\n }\n // Network error before any response — safe to retry.\n if (attempt < maxRetries) {\n await this.backoff(undefined, attempt, options?.signal);\n attempt++;\n continue;\n }\n throw new ConnectionError(e?.message ?? \"Network request failed\", { raw: e });\n } finally {\n done();\n }\n }\n }\n\n private async backoff(err: CurvetError | undefined, attempt: number, signal?: AbortSignal) {\n let delay = fullJitterBackoff(attempt);\n if (err instanceof RateLimitError && err.retryAfterMs != null) {\n delay = Math.min(err.retryAfterMs, 30000);\n }\n await sleep(delay, signal);\n }\n\n private buildUrl(path: string, query?: Record<string, string | number | undefined>) {\n const base = this.opts.baseURL.replace(/\\/$/, \"\");\n let url = base + (path.startsWith(\"/\") ? path : \"/\" + path);\n if (query) {\n const qs = Object.entries(query)\n .filter(([, v]) => v !== undefined)\n .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)\n .join(\"&\");\n if (qs) url += \"?\" + qs;\n }\n return url;\n }\n\n /** Combine a per-request timeout with an optional user AbortSignal. */\n private makeSignal(timeout: number, userSignal?: AbortSignal) {\n const controller = new AbortController();\n const onUserAbort = () => controller.abort();\n if (userSignal) {\n if (userSignal.aborted) controller.abort();\n else userSignal.addEventListener(\"abort\", onUserAbort, { once: true });\n }\n const timer = setTimeout(() => controller.abort(), timeout);\n return {\n signal: controller.signal,\n done: () => {\n clearTimeout(timer);\n userSignal?.removeEventListener(\"abort\", onUserAbort);\n },\n };\n }\n}\n\nfunction safeJson(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n","import type { HttpClient } from \"../core/http\";\nimport type { ChatCreateParams, ChatResponse } from \"../types/chat\";\nimport type { RequestOptions } from \"../types/common\";\n\nexport class Chat {\n constructor(private client: HttpClient) {}\n\n /** Create a chat completion. Synchronous — resolves with the model's reply. */\n create(params: ChatCreateParams, options?: RequestOptions): Promise<ChatResponse> {\n return this.client.request<ChatResponse>({\n method: \"POST\",\n path: \"/chat\",\n body: params,\n options,\n });\n }\n}\n","import type { HttpClient } from \"../core/http\";\nimport type { ImageGenerateParams, ImageResponse } from \"../types/image\";\nimport type { RequestOptions } from \"../types/common\";\n\nexport class Images {\n constructor(private client: HttpClient) {}\n\n /** Generate an image from a prompt. Synchronous — resolves with `imageUrl`. */\n generate(params: ImageGenerateParams, options?: RequestOptions): Promise<ImageResponse> {\n return this.client.request<ImageResponse>({\n method: \"POST\",\n path: \"/image\",\n body: params,\n options,\n });\n }\n}\n","import type { MediaJob, JobStatus } from \"../types/job\";\n\n/**\n * The API returns the output URL under three different keys depending on the\n * endpoint: `videoUrl` (/video), `audioUrl` (/audio), `modelUrl` (/3d), and\n * `output.mediaUrl` (GET /jobs/:id). Unify them.\n */\nfunction pickMediaUrl(body: Record<string, any>): string | undefined {\n return (\n body?.videoUrl ??\n body?.audioUrl ??\n body?.modelUrl ??\n body?.output?.mediaUrl ??\n body?.mediaUrl ??\n undefined\n );\n}\n\n/**\n * Normalize a POST /video|/audio|/3d response, which is EITHER a 200 with the\n * final media URL (job already done) OR a 202 with just a `jobId` to poll.\n */\nexport function normalizeMediaPost(body: unknown): MediaJob {\n const b = (body ?? {}) as Record<string, any>;\n const mediaUrl = pickMediaUrl(b);\n const jobId = b.jobId ?? b.metadata?.jobId;\n const status: JobStatus = mediaUrl\n ? \"completed\"\n : b.status === \"failed\"\n ? \"failed\"\n : \"processing\";\n return {\n jobId,\n status,\n mediaUrl,\n usage: b.usage,\n metadata: b.metadata,\n error: b.error ?? null,\n raw: body,\n };\n}\n\n/** Normalize a GET /jobs/:id response. */\nexport function normalizeJob(body: unknown): MediaJob {\n const b = (body ?? {}) as Record<string, any>;\n const mediaUrl = pickMediaUrl(b);\n const status: JobStatus = (b.status as JobStatus) ?? (mediaUrl ? \"completed\" : \"processing\");\n return {\n jobId: b.jobId,\n status,\n progress: b.progress,\n mediaUrl,\n metadata: b.output?.metadata ?? b.metadata,\n error: b.error ?? null,\n cost: b.cost,\n eta: b.eta,\n raw: body,\n };\n}\n","import { sleep } from \"./retry\";\n\n/** Thrown internally when a poll loop exceeds its timeout. */\nexport class PollTimeoutError extends Error {\n constructor(message = \"Polling timed out\") {\n super(message);\n this.name = \"PollTimeoutError\";\n }\n}\n\nexport interface PollConfig<T> {\n isTerminal: (result: T) => boolean;\n intervalMs: number;\n timeoutMs: number;\n signal?: AbortSignal;\n onTick?: (result: T) => void;\n}\n\n/**\n * Repeatedly call `fn` until `isTerminal` is true (resolves the result) or the\n * timeout elapses (throws PollTimeoutError). Polls immediately, then every\n * `intervalMs`. Honors an AbortSignal between ticks.\n */\nexport async function pollUntil<T>(fn: () => Promise<T>, cfg: PollConfig<T>): Promise<T> {\n const start = Date.now();\n for (;;) {\n const result = await fn();\n cfg.onTick?.(result);\n if (cfg.isTerminal(result)) return result;\n const elapsed = Date.now() - start;\n if (elapsed >= cfg.timeoutMs) throw new PollTimeoutError();\n const remaining = cfg.timeoutMs - elapsed;\n await sleep(Math.min(cfg.intervalMs, Math.max(0, remaining)), cfg.signal);\n }\n}\n","import type { HttpClient } from \"../core/http\";\nimport type { MediaJob, PollOptions } from \"../types/job\";\nimport type { RequestOptions } from \"../types/common\";\nimport { normalizeJob } from \"../core/normalize\";\nimport { pollUntil, PollTimeoutError } from \"../core/poll\";\nimport { JobFailedError, JobTimeoutError } from \"../core/errors\";\n\nexport interface JobDefaults {\n pollIntervalMs: number;\n pollTimeoutMs: number;\n}\n\nexport class Jobs {\n constructor(\n private client: HttpClient,\n private defaults: JobDefaults,\n ) {}\n\n /** Fetch the current status of an async job once (no polling). */\n async retrieve(jobId: string, options?: RequestOptions): Promise<MediaJob> {\n const body = await this.client.request({\n method: \"GET\",\n path: `/jobs/${encodeURIComponent(jobId)}`,\n options,\n });\n return normalizeJob(body);\n }\n\n /** Get a {@link Job} handle to poll/await an existing job by id. */\n handle(jobId: string): Job {\n return new Job(jobId, this.client, this.defaults);\n }\n}\n\n/** A handle to a single async media job. */\nexport class Job {\n constructor(\n readonly id: string,\n private client: HttpClient,\n private defaults: JobDefaults,\n ) {}\n\n /** One status fetch (no polling). */\n retrieve(options?: RequestOptions): Promise<MediaJob> {\n return new Jobs(this.client, this.defaults).retrieve(this.id, options);\n }\n\n /**\n * Poll until the job reaches a terminal state.\n * Resolves with the completed job, or throws JobFailedError / JobTimeoutError.\n */\n async wait(opts: PollOptions = {}): Promise<MediaJob> {\n const intervalMs = opts.pollIntervalMs ?? this.defaults.pollIntervalMs;\n const timeoutMs = opts.pollTimeoutMs ?? this.defaults.pollTimeoutMs;\n\n let result: MediaJob;\n try {\n result = await pollUntil(() => this.retrieve({ signal: opts.signal }), {\n intervalMs,\n timeoutMs,\n signal: opts.signal,\n isTerminal: (r) => r.status === \"completed\" || r.status === \"failed\",\n onTick: (r) => opts.onProgress?.(r.progress ?? 0, r.eta),\n });\n } catch (e) {\n if (e instanceof PollTimeoutError) {\n throw new JobTimeoutError(\n `Job ${this.id} did not complete within ${timeoutMs}ms`,\n this.id,\n );\n }\n throw e;\n }\n\n if (result.status === \"failed\") {\n throw new JobFailedError(result.error || `Job ${this.id} failed`, this.id, {\n raw: result.raw,\n });\n }\n return result;\n }\n}\n","import type { HttpClient } from \"../core/http\";\nimport type { MediaJob, VideoGenerateParams, PollOptions } from \"../types/job\";\nimport type { RequestOptions } from \"../types/common\";\nimport { normalizeMediaPost } from \"../core/normalize\";\nimport { Job, type JobDefaults } from \"./jobs\";\nimport { CurvetError } from \"../core/errors\";\n\n/**\n * Video generation. Backed by an async job queue: `generate()` submits and\n * polls to completion (the common case); `submit()` fires and returns the job\n * handle for manual polling.\n *\n * The same implementation backs audio and 3D (v1.1) via the `path` arg.\n */\nexport class Video {\n constructor(\n private client: HttpClient,\n private defaults: JobDefaults,\n private path: string = \"/video\",\n ) {}\n\n /**\n * Submit a job WITHOUT polling. Returns once the server responds — either the\n * 200 fast-path (already done) or a 202 with a jobId.\n *\n * The media POST long-polls server-side and can block well past a normal\n * request timeout, so we default its timeout to the poll budget and disable\n * auto-retry (a retried POST would enqueue a duplicate, double-charged job).\n */\n async submit(params: VideoGenerateParams, options?: RequestOptions): Promise<MediaJob> {\n const reqOptions: RequestOptions = {\n ...options,\n timeout: options?.timeout ?? this.defaults.pollTimeoutMs,\n maxRetries: options?.maxRetries ?? 0,\n };\n const body = await this.client.request({\n method: \"POST\",\n path: this.path,\n body: params,\n options: reqOptions,\n });\n return normalizeMediaPost(body);\n }\n\n /**\n * Submit and resolve to the finished media. Handles the 200-vs-202 split and\n * polls `/jobs/:id` internally. Throws JobFailedError / JobTimeoutError.\n */\n async generate(\n params: VideoGenerateParams,\n options?: RequestOptions & PollOptions,\n ): Promise<MediaJob> {\n const submitted = await this.submit(params, options);\n if (submitted.status === \"completed\" || submitted.status === \"failed\") {\n return submitted;\n }\n if (!submitted.jobId) {\n throw new CurvetError(\"Async job did not return a jobId to poll\", {\n raw: submitted.raw,\n });\n }\n const job = new Job(submitted.jobId, this.client, this.defaults);\n return job.wait({\n pollIntervalMs: options?.pollIntervalMs,\n pollTimeoutMs: options?.pollTimeoutMs,\n signal: options?.signal,\n onProgress: options?.onProgress,\n });\n }\n}\n","import type { HttpClient } from \"../core/http\";\nimport type { ModelInfo, RateLimits } from \"../types/models\";\nimport type { RequestOptions } from \"../types/common\";\n\ninterface ModelsListResponse {\n success: boolean;\n models: ModelInfo[];\n rateLimits: RateLimits;\n}\n\nexport interface ModelsListOptions extends RequestOptions {\n /** Filter to a single model type (e.g. \"chat\", \"image\", \"video\"). */\n type?: string;\n /** Bypass the in-memory cache and fetch fresh. */\n refresh?: boolean;\n}\n\n/**\n * Live model catalog. The list is dynamic and per-app filtered server-side, so\n * it is always fetched (with a short in-memory cache), never hardcoded.\n */\nexport class Models {\n private cache?: { at: number; data: ModelsListResponse };\n\n constructor(\n private client: HttpClient,\n private cacheTtlMs = 60_000,\n ) {}\n\n private async load(options?: RequestOptions): Promise<ModelsListResponse> {\n return this.client.request<ModelsListResponse>({\n method: \"GET\",\n path: \"/models\",\n options,\n });\n }\n\n private async ensure(options?: ModelsListOptions): Promise<ModelsListResponse> {\n const stale = !this.cache || Date.now() - this.cache.at > this.cacheTtlMs;\n if (options?.refresh || stale) {\n this.cache = { at: Date.now(), data: await this.load(options) };\n }\n return this.cache!.data;\n }\n\n /** List available models, optionally filtered by `type`. */\n async list(options?: ModelsListOptions): Promise<ModelInfo[]> {\n const data = await this.ensure(options);\n const models = data.models ?? [];\n return options?.type ? models.filter((m) => m.type === options.type) : models;\n }\n\n /** Find a single model by id (or undefined). */\n async get(id: string, options?: ModelsListOptions): Promise<ModelInfo | undefined> {\n return (await this.list(options)).find((m) => m.id === id);\n }\n\n /** The app's rate limits as reported by GET /models. */\n async rateLimits(options?: ModelsListOptions): Promise<RateLimits | undefined> {\n return (await this.ensure(options)).rateLimits;\n }\n}\n","import type { HttpClient } from \"../core/http\";\nimport type { RequestOptions } from \"../types/common\";\n\nexport interface BalanceInfo {\n walletBalance?: number;\n totalAvailableUSD: number;\n totalPoints?: number;\n breakdown?: {\n walletCredits?: number;\n totalCredits?: number;\n organizationLimit?: number;\n monthlyUsed?: number;\n isEnterprise?: boolean;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n}\n\nexport class Balance {\n constructor(private client: HttpClient) {}\n\n /** Get the current credit balance for the app owner. */\n async get(options?: RequestOptions): Promise<BalanceInfo> {\n const body = await this.client.request<{ success: boolean; balance: BalanceInfo }>({\n method: \"GET\",\n path: \"/balance\",\n options,\n });\n return body.balance;\n }\n}\n","import { HttpClient } from \"./core/http\";\nimport { CurvetError } from \"./core/errors\";\nimport type { FetchLike } from \"./types/common\";\nimport { Chat } from \"./resources/chat\";\nimport { Images } from \"./resources/image\";\nimport { Video } from \"./resources/video\";\nimport { Jobs } from \"./resources/jobs\";\nimport { Models } from \"./resources/models\";\nimport { Balance } from \"./resources/balance\";\n\nexport const DEFAULT_BASE_URL = \"https://curvet.ai/api/v1/playground\";\n\nexport interface CurvetOptions {\n /** Your app key. Falls back to the CURVET_APP_KEY env var. */\n appKey?: string;\n /** Override the gateway base URL (defaults to production). */\n baseURL?: string;\n /** Per-request timeout in ms (default 60000). */\n timeout?: number;\n /** Max automatic retries for 429/5xx and network errors (default 2). */\n maxRetries?: number;\n /** Inject a fetch implementation (defaults to global fetch on Node 18+). */\n fetch?: FetchLike;\n /** Default poll interval for async media jobs, in ms (default 2500). */\n defaultPollIntervalMs?: number;\n /** Default poll timeout for async media jobs, in ms (default 180000). */\n defaultPollTimeoutMs?: number;\n}\n\n/**\n * The Curvet client. One instance per app key.\n *\n * ```ts\n * const curvet = new Curvet({ appKey: process.env.CURVET_APP_KEY });\n * const { response } = await curvet.chat.create({\n * model: \"gpt-4o-mini\",\n * messages: [{ role: \"user\", content: \"hi\" }],\n * });\n * ```\n */\nexport class Curvet {\n readonly chat: Chat;\n readonly image: Images;\n readonly video: Video;\n readonly jobs: Jobs;\n readonly models: Models;\n readonly balance: Balance;\n\n constructor(options: CurvetOptions = {}) {\n const appKey = options.appKey ?? envKey();\n if (!appKey) {\n throw new CurvetError(\n \"Missing Curvet app key. Pass { appKey } or set the CURVET_APP_KEY environment variable.\",\n );\n }\n const fetchImpl = options.fetch ?? defaultFetch();\n if (!fetchImpl) {\n throw new CurvetError(\n \"No fetch implementation available. Use Node 18+ or pass { fetch }.\",\n );\n }\n\n const client = new HttpClient({\n appKey,\n baseURL: options.baseURL ?? DEFAULT_BASE_URL,\n timeout: options.timeout ?? 60_000,\n maxRetries: options.maxRetries ?? 2,\n fetch: fetchImpl,\n });\n\n const jobDefaults = {\n pollIntervalMs: options.defaultPollIntervalMs ?? 2500,\n pollTimeoutMs: options.defaultPollTimeoutMs ?? 180_000,\n };\n\n this.chat = new Chat(client);\n this.image = new Images(client);\n this.jobs = new Jobs(client, jobDefaults);\n this.video = new Video(client, jobDefaults);\n this.models = new Models(client);\n this.balance = new Balance(client);\n }\n}\n\nfunction envKey(): string | undefined {\n return typeof process !== \"undefined\" ? process.env?.CURVET_APP_KEY : undefined;\n}\n\nfunction defaultFetch(): FetchLike | undefined {\n const f = (globalThis as { fetch?: unknown }).fetch;\n return typeof f === \"function\" ? (f.bind(globalThis) as FetchLike) : undefined;\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@curvet/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Official TypeScript SDK for the Curvet Unified Playground API (chat, image, and video generation across providers, one API key).",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Curvet <hello@curvet.in>",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/index.cjs",
|
|
9
|
+
"module": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"require": "./dist/index.cjs"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"README.md",
|
|
21
|
+
"LICENSE"
|
|
22
|
+
],
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=18"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsup",
|
|
28
|
+
"test": "vitest run",
|
|
29
|
+
"test:watch": "vitest",
|
|
30
|
+
"typecheck": "tsc --noEmit",
|
|
31
|
+
"prepublishOnly": "npm run typecheck && npm run test && npm run build"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"curvet",
|
|
35
|
+
"ai",
|
|
36
|
+
"sdk",
|
|
37
|
+
"llm",
|
|
38
|
+
"chat",
|
|
39
|
+
"image-generation",
|
|
40
|
+
"video-generation",
|
|
41
|
+
"openai",
|
|
42
|
+
"anthropic"
|
|
43
|
+
],
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "git+https://github.com/Curvet-in/curvet-sdk.git"
|
|
47
|
+
},
|
|
48
|
+
"homepage": "https://github.com/Curvet-in/curvet-sdk#readme",
|
|
49
|
+
"bugs": {
|
|
50
|
+
"url": "https://github.com/Curvet-in/curvet-sdk/issues"
|
|
51
|
+
},
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/node": "^20.14.0",
|
|
57
|
+
"tsup": "^8.1.0",
|
|
58
|
+
"typescript": "^5.4.5",
|
|
59
|
+
"vitest": "^1.6.0"
|
|
60
|
+
}
|
|
61
|
+
}
|