corent-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/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # Corent SDK
2
+
3
+ One API for AI **image, video, and voice** generation. You pick a quality tier; Corent's router picks the best live model, reroutes failures, verifies the output, and returns the exact charge on every response. Failed generations are never billed.
4
+
5
+ ```bash
6
+ npm install corent-sdk
7
+ ```
8
+
9
+ ```ts
10
+ import { Corent } from "corent-sdk";
11
+
12
+ const client = new Corent("co_live_..."); // get a key at https://corent.tech
13
+
14
+ const image = await client.images.generate("a lighthouse at dusk", {
15
+ tier: "premium",
16
+ aspectRatio: "9:16",
17
+ });
18
+ console.log(image.url, image.width, image.height, image.costCents);
19
+
20
+ const video = await client.videos.generate("a paper boat drifting across a puddle", {
21
+ tier: "premium",
22
+ durationS: 5,
23
+ resolution: "1080p",
24
+ });
25
+ console.log(video.url, video.resolution, video.costCents);
26
+
27
+ const speech = await client.speech.generate("Welcome to Corent.");
28
+ console.log(speech.url, speech.costCents);
29
+ ```
30
+
31
+ ## What the SDK handles for you
32
+
33
+ - **Timeout-safe renders** — images submit as background jobs and are polled; a network hiccup can never lose a finished (and billed) result.
34
+ - **Safe retries** — every generate call carries an auto idempotency key; retries can never double-charge.
35
+ - **Backoff** — 429/5xx retried with `Retry-After` respected.
36
+ - **Honest receipts** — `width`/`height` are the *measured* pixels of the delivered file; `costCents` is the exact charge.
37
+
38
+ ## Fine-grained control
39
+
40
+ ```ts
41
+ const job = await client.images.generate("...", { tier: "max_pro", wait: false });
42
+ const done = await client.jobs.wait(job.id); // resume any time
43
+
44
+ await client.tiers(); // live catalog with honest min–max price ranges
45
+ await client.balanceCents(); // prepaid balance
46
+ ```
47
+
48
+ Tiers: `air` | `lite` | `premium` | `pro` | `max_pro` — see [corent.tech/pricing](https://corent.tech/pricing). Docs: [corent.tech/docs](https://corent.tech/docs). MCP server for agents: [`corent-mcp`](https://www.npmjs.com/package/corent-mcp).
package/dist/index.cjs ADDED
@@ -0,0 +1,260 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+
19
+ // src/index.ts
20
+ var index_exports = {};
21
+ __export(index_exports, {
22
+ Corent: () => Corent,
23
+ CorentError: () => CorentError,
24
+ GenerationFailedError: () => GenerationFailedError,
25
+ InsufficientBalanceError: () => InsufficientBalanceError,
26
+ InvalidRequestError: () => InvalidRequestError,
27
+ RateLimitedError: () => RateLimitedError
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+ var DEFAULT_BASE_URL = "https://api.corent.tech";
31
+ var MAX_RETRIES = 3;
32
+ var POLL_INTERVAL_MS = 3e3;
33
+ var DEFAULT_WAIT_TIMEOUT_MS = 9e5;
34
+ var CorentError = class extends Error {
35
+ constructor(message, statusCode) {
36
+ super(message);
37
+ this.statusCode = statusCode;
38
+ this.name = "CorentError";
39
+ }
40
+ statusCode;
41
+ };
42
+ var InvalidRequestError = class extends CorentError {
43
+ name = "InvalidRequestError";
44
+ };
45
+ var InsufficientBalanceError = class extends CorentError {
46
+ name = "InsufficientBalanceError";
47
+ };
48
+ var RateLimitedError = class extends CorentError {
49
+ name = "RateLimitedError";
50
+ };
51
+ var GenerationFailedError = class extends CorentError {
52
+ name = "GenerationFailedError";
53
+ };
54
+ var Corent = class {
55
+ baseUrl;
56
+ apiKey;
57
+ fetchFn;
58
+ images;
59
+ videos;
60
+ speech;
61
+ jobs;
62
+ constructor(apiKey, options = {}) {
63
+ this.apiKey = apiKey;
64
+ this.baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;
65
+ this.fetchFn = options.fetch ?? fetch;
66
+ this.images = new Images(this);
67
+ this.videos = new Videos(this);
68
+ this.speech = new Speech(this);
69
+ this.jobs = new Jobs(this);
70
+ }
71
+ /** Prepaid balance in cents. */
72
+ async balanceCents() {
73
+ const r = await this.request("GET", "/v1/account/balance");
74
+ return r.balance_cents;
75
+ }
76
+ /** The live tier catalog with honest min–max price ranges. Public. */
77
+ async tiers() {
78
+ const r = await this.request("GET", "/v1/tiers");
79
+ return r.tiers;
80
+ }
81
+ /** @internal */
82
+ async request(method, path, body, idempotent = false) {
83
+ const headers = {
84
+ Authorization: `Bearer ${this.apiKey}`,
85
+ "User-Agent": "corent-js/0.1.0"
86
+ };
87
+ if (body) headers["Content-Type"] = "application/json";
88
+ if (idempotent) headers["Idempotency-Key"] = crypto.randomUUID();
89
+ let lastError;
90
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
91
+ let resp;
92
+ try {
93
+ resp = await this.fetchFn(`${this.baseUrl}${path}`, {
94
+ method,
95
+ headers,
96
+ body: body ? JSON.stringify(body) : void 0
97
+ });
98
+ } catch (err) {
99
+ lastError = err;
100
+ await sleep(Math.min(2 ** attempt * 1e3, 8e3));
101
+ continue;
102
+ }
103
+ if (resp.status < 400) return await resp.json();
104
+ if (resp.status === 429) {
105
+ const retryAfter = Number(resp.headers.get("Retry-After") ?? 2 ** (attempt + 1));
106
+ if (attempt < MAX_RETRIES) {
107
+ await sleep(Math.min(retryAfter * 1e3, 3e4));
108
+ continue;
109
+ }
110
+ throw new RateLimitedError("rate limited", 429);
111
+ }
112
+ if ((resp.status === 502 || resp.status === 503) && attempt < MAX_RETRIES) {
113
+ await sleep(Math.min(2 ** (attempt + 1) * 1e3, 8e3));
114
+ continue;
115
+ }
116
+ throw await errorFor(resp);
117
+ }
118
+ throw new CorentError(`request failed after ${MAX_RETRIES} retries: ${lastError}`);
119
+ }
120
+ /** @internal */
121
+ async waitForJob(jobId, timeoutMs) {
122
+ const deadline = Date.now() + timeoutMs;
123
+ while (Date.now() < deadline) {
124
+ const job = await this.request("GET", `/v1/jobs/${jobId}`);
125
+ if (job.status === "completed") return job;
126
+ if (job.status === "failed") {
127
+ throw new GenerationFailedError(job.error ?? "generation_failed");
128
+ }
129
+ await sleep(POLL_INTERVAL_MS);
130
+ }
131
+ throw new CorentError(
132
+ `job ${jobId} still processing after ${Math.round(timeoutMs / 1e3)}s; poll client.jobs.get("${jobId}") to resume`
133
+ );
134
+ }
135
+ };
136
+ var Images = class {
137
+ constructor(c) {
138
+ this.c = c;
139
+ }
140
+ c;
141
+ /** Render an image. Submits as a background job and polls, so a client
142
+ * timeout can never lose a finished render. Pass wait:false for the Job. */
143
+ async generate(prompt, options = {}) {
144
+ const body = {
145
+ prompt,
146
+ aspect_ratio: options.aspectRatio ?? "1:1",
147
+ async: true
148
+ };
149
+ if (options.tier) body.tier = options.tier;
150
+ if (options.style) body.style = options.style;
151
+ const submitted = await this.c.request("POST", "/v1/images/generate", body, true);
152
+ if (submitted.status === "completed") return imageFromJob(submitted);
153
+ if (options.wait === false) {
154
+ return { id: submitted.id, status: submitted.status, raw: submitted };
155
+ }
156
+ return imageFromJob(await this.c.waitForJob(submitted.id, options.timeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS));
157
+ }
158
+ };
159
+ var Videos = class {
160
+ constructor(c) {
161
+ this.c = c;
162
+ }
163
+ c;
164
+ /** Render a video (1–5 minutes typical). resolution: 720p | 1080p | 4k —
165
+ * tier-capped, clamped down rather than rejected. imageUrl animates an
166
+ * existing image (image-to-video). */
167
+ async generate(prompt, options = {}) {
168
+ const body = {
169
+ prompt,
170
+ aspect_ratio: options.aspectRatio ?? "16:9"
171
+ };
172
+ if (options.tier) body.tier = options.tier;
173
+ if (options.durationS !== void 0) body.duration_s = options.durationS;
174
+ if (options.resolution) body.resolution = options.resolution;
175
+ if (options.imageUrl) body.image_url = options.imageUrl;
176
+ const submitted = await this.c.request("POST", "/v1/videos/generate", body, true);
177
+ if (options.wait === false) {
178
+ return { id: submitted.id, status: submitted.status, raw: submitted };
179
+ }
180
+ return videoFromJob(await this.c.waitForJob(submitted.id, options.timeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS));
181
+ }
182
+ };
183
+ var Speech = class {
184
+ constructor(c) {
185
+ this.c = c;
186
+ }
187
+ c;
188
+ /** Text to speech; synchronous, returns the finished audio and exact charge. */
189
+ async generate(text, options = {}) {
190
+ const body = { text };
191
+ if (options.voiceId) body.voice_id = options.voiceId;
192
+ const r = await this.c.request("POST", "/v1/audio/speech", body, true);
193
+ const meta = r.meta ?? {};
194
+ return { id: r.id, url: r.audio_url, model: meta.model, costCents: meta.cost_cents };
195
+ }
196
+ };
197
+ var Jobs = class {
198
+ constructor(c) {
199
+ this.c = c;
200
+ }
201
+ c;
202
+ async get(jobId) {
203
+ const raw = await this.c.request("GET", `/v1/jobs/${jobId}`);
204
+ return { id: raw.id, status: raw.status, raw };
205
+ }
206
+ async wait(jobId, timeoutMs = DEFAULT_WAIT_TIMEOUT_MS) {
207
+ const raw = await this.c.waitForJob(jobId, timeoutMs);
208
+ return { id: raw.id, status: raw.status, raw };
209
+ }
210
+ };
211
+ async function errorFor(resp) {
212
+ let message;
213
+ try {
214
+ const parsed = await resp.json();
215
+ message = typeof parsed.detail === "string" ? parsed.detail : JSON.stringify(parsed.detail ?? parsed);
216
+ } catch {
217
+ message = `HTTP ${resp.status}`;
218
+ }
219
+ if (resp.status === 402) return new InsufficientBalanceError(message, 402);
220
+ if (resp.status === 400 || resp.status === 422) return new InvalidRequestError(message, resp.status);
221
+ return new CorentError(message, resp.status);
222
+ }
223
+ function imageFromJob(job) {
224
+ const image = (job.images ?? [{}])[0];
225
+ const meta = job.meta ?? {};
226
+ return {
227
+ id: job.id,
228
+ url: image.url,
229
+ width: image.width,
230
+ height: image.height,
231
+ model: meta.model,
232
+ costCents: meta.cost_cents
233
+ };
234
+ }
235
+ function videoFromJob(job) {
236
+ const video = (job.videos ?? [{}])[0];
237
+ const meta = job.meta ?? {};
238
+ return {
239
+ id: job.id,
240
+ url: video.url,
241
+ width: video.width,
242
+ height: video.height,
243
+ resolution: video.resolution,
244
+ durationS: video.duration_s,
245
+ model: meta.model,
246
+ costCents: meta.cost_cents
247
+ };
248
+ }
249
+ function sleep(ms) {
250
+ return new Promise((resolve) => setTimeout(resolve, ms));
251
+ }
252
+ // Annotate the CommonJS export names for ESM import in node:
253
+ 0 && (module.exports = {
254
+ Corent,
255
+ CorentError,
256
+ GenerationFailedError,
257
+ InsufficientBalanceError,
258
+ InvalidRequestError,
259
+ RateLimitedError
260
+ });
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Corent TypeScript SDK — one API for AI image, video, and voice.
3
+ *
4
+ * import { Corent } from "corent-sdk";
5
+ * const client = new Corent("co_live_...");
6
+ * const image = await client.images.generate("a lighthouse", { tier: "premium" });
7
+ *
8
+ * What it handles for you: auth, auto idempotency keys (retries can never
9
+ * double-charge), 429/5xx backoff, and job polling. Images submit as
10
+ * background jobs so a network timeout can never lose a finished render.
11
+ */
12
+ type Tier = "air" | "lite" | "premium" | "pro" | "max_pro";
13
+ interface GeneratedImage {
14
+ id: string;
15
+ url: string;
16
+ /** MEASURED pixels of the delivered file — Corent parses the file, never echoes the request. */
17
+ width?: number;
18
+ height?: number;
19
+ model?: string;
20
+ costCents?: number;
21
+ }
22
+ interface GeneratedVideo {
23
+ id: string;
24
+ url: string;
25
+ width?: number;
26
+ height?: number;
27
+ resolution?: string;
28
+ durationS?: number;
29
+ model?: string;
30
+ costCents?: number;
31
+ }
32
+ interface GeneratedSpeech {
33
+ id: string;
34
+ url: string;
35
+ model?: string;
36
+ costCents?: number;
37
+ }
38
+ interface Job {
39
+ id: string;
40
+ status: "processing" | "completed" | "failed";
41
+ raw: Record<string, unknown>;
42
+ }
43
+ declare class CorentError extends Error {
44
+ statusCode?: number;
45
+ constructor(message: string, statusCode?: number);
46
+ }
47
+ declare class InvalidRequestError extends CorentError {
48
+ name: string;
49
+ }
50
+ declare class InsufficientBalanceError extends CorentError {
51
+ name: string;
52
+ }
53
+ declare class RateLimitedError extends CorentError {
54
+ name: string;
55
+ }
56
+ declare class GenerationFailedError extends CorentError {
57
+ name: string;
58
+ }
59
+ interface ClientOptions {
60
+ baseUrl?: string;
61
+ fetch?: typeof fetch;
62
+ }
63
+ declare class Corent {
64
+ private baseUrl;
65
+ private apiKey;
66
+ private fetchFn;
67
+ readonly images: Images;
68
+ readonly videos: Videos;
69
+ readonly speech: Speech;
70
+ readonly jobs: Jobs;
71
+ constructor(apiKey: string, options?: ClientOptions);
72
+ /** Prepaid balance in cents. */
73
+ balanceCents(): Promise<number>;
74
+ /** The live tier catalog with honest min–max price ranges. Public. */
75
+ tiers(): Promise<Record<string, unknown>[]>;
76
+ /** @internal */
77
+ request(method: string, path: string, body?: Record<string, unknown>, idempotent?: boolean): Promise<Record<string, any>>;
78
+ /** @internal */
79
+ waitForJob(jobId: string, timeoutMs: number): Promise<Record<string, any>>;
80
+ }
81
+ declare class Images {
82
+ private c;
83
+ constructor(c: Corent);
84
+ /** Render an image. Submits as a background job and polls, so a client
85
+ * timeout can never lose a finished render. Pass wait:false for the Job. */
86
+ generate(prompt: string, options?: {
87
+ tier?: Tier;
88
+ style?: string;
89
+ aspectRatio?: string;
90
+ wait?: false | true;
91
+ timeoutMs?: number;
92
+ }): Promise<GeneratedImage | Job>;
93
+ }
94
+ declare class Videos {
95
+ private c;
96
+ constructor(c: Corent);
97
+ /** Render a video (1–5 minutes typical). resolution: 720p | 1080p | 4k —
98
+ * tier-capped, clamped down rather than rejected. imageUrl animates an
99
+ * existing image (image-to-video). */
100
+ generate(prompt: string, options?: {
101
+ tier?: Tier;
102
+ aspectRatio?: string;
103
+ durationS?: number;
104
+ resolution?: "720p" | "1080p" | "4k";
105
+ imageUrl?: string;
106
+ wait?: false | true;
107
+ timeoutMs?: number;
108
+ }): Promise<GeneratedVideo | Job>;
109
+ }
110
+ declare class Speech {
111
+ private c;
112
+ constructor(c: Corent);
113
+ /** Text to speech; synchronous, returns the finished audio and exact charge. */
114
+ generate(text: string, options?: {
115
+ voiceId?: string;
116
+ }): Promise<GeneratedSpeech>;
117
+ }
118
+ declare class Jobs {
119
+ private c;
120
+ constructor(c: Corent);
121
+ get(jobId: string): Promise<Job>;
122
+ wait(jobId: string, timeoutMs?: number): Promise<Job>;
123
+ }
124
+
125
+ export { Corent, CorentError, type GeneratedImage, type GeneratedSpeech, type GeneratedVideo, GenerationFailedError, InsufficientBalanceError, InvalidRequestError, type Job, RateLimitedError, type Tier };
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Corent TypeScript SDK — one API for AI image, video, and voice.
3
+ *
4
+ * import { Corent } from "corent-sdk";
5
+ * const client = new Corent("co_live_...");
6
+ * const image = await client.images.generate("a lighthouse", { tier: "premium" });
7
+ *
8
+ * What it handles for you: auth, auto idempotency keys (retries can never
9
+ * double-charge), 429/5xx backoff, and job polling. Images submit as
10
+ * background jobs so a network timeout can never lose a finished render.
11
+ */
12
+ type Tier = "air" | "lite" | "premium" | "pro" | "max_pro";
13
+ interface GeneratedImage {
14
+ id: string;
15
+ url: string;
16
+ /** MEASURED pixels of the delivered file — Corent parses the file, never echoes the request. */
17
+ width?: number;
18
+ height?: number;
19
+ model?: string;
20
+ costCents?: number;
21
+ }
22
+ interface GeneratedVideo {
23
+ id: string;
24
+ url: string;
25
+ width?: number;
26
+ height?: number;
27
+ resolution?: string;
28
+ durationS?: number;
29
+ model?: string;
30
+ costCents?: number;
31
+ }
32
+ interface GeneratedSpeech {
33
+ id: string;
34
+ url: string;
35
+ model?: string;
36
+ costCents?: number;
37
+ }
38
+ interface Job {
39
+ id: string;
40
+ status: "processing" | "completed" | "failed";
41
+ raw: Record<string, unknown>;
42
+ }
43
+ declare class CorentError extends Error {
44
+ statusCode?: number;
45
+ constructor(message: string, statusCode?: number);
46
+ }
47
+ declare class InvalidRequestError extends CorentError {
48
+ name: string;
49
+ }
50
+ declare class InsufficientBalanceError extends CorentError {
51
+ name: string;
52
+ }
53
+ declare class RateLimitedError extends CorentError {
54
+ name: string;
55
+ }
56
+ declare class GenerationFailedError extends CorentError {
57
+ name: string;
58
+ }
59
+ interface ClientOptions {
60
+ baseUrl?: string;
61
+ fetch?: typeof fetch;
62
+ }
63
+ declare class Corent {
64
+ private baseUrl;
65
+ private apiKey;
66
+ private fetchFn;
67
+ readonly images: Images;
68
+ readonly videos: Videos;
69
+ readonly speech: Speech;
70
+ readonly jobs: Jobs;
71
+ constructor(apiKey: string, options?: ClientOptions);
72
+ /** Prepaid balance in cents. */
73
+ balanceCents(): Promise<number>;
74
+ /** The live tier catalog with honest min–max price ranges. Public. */
75
+ tiers(): Promise<Record<string, unknown>[]>;
76
+ /** @internal */
77
+ request(method: string, path: string, body?: Record<string, unknown>, idempotent?: boolean): Promise<Record<string, any>>;
78
+ /** @internal */
79
+ waitForJob(jobId: string, timeoutMs: number): Promise<Record<string, any>>;
80
+ }
81
+ declare class Images {
82
+ private c;
83
+ constructor(c: Corent);
84
+ /** Render an image. Submits as a background job and polls, so a client
85
+ * timeout can never lose a finished render. Pass wait:false for the Job. */
86
+ generate(prompt: string, options?: {
87
+ tier?: Tier;
88
+ style?: string;
89
+ aspectRatio?: string;
90
+ wait?: false | true;
91
+ timeoutMs?: number;
92
+ }): Promise<GeneratedImage | Job>;
93
+ }
94
+ declare class Videos {
95
+ private c;
96
+ constructor(c: Corent);
97
+ /** Render a video (1–5 minutes typical). resolution: 720p | 1080p | 4k —
98
+ * tier-capped, clamped down rather than rejected. imageUrl animates an
99
+ * existing image (image-to-video). */
100
+ generate(prompt: string, options?: {
101
+ tier?: Tier;
102
+ aspectRatio?: string;
103
+ durationS?: number;
104
+ resolution?: "720p" | "1080p" | "4k";
105
+ imageUrl?: string;
106
+ wait?: false | true;
107
+ timeoutMs?: number;
108
+ }): Promise<GeneratedVideo | Job>;
109
+ }
110
+ declare class Speech {
111
+ private c;
112
+ constructor(c: Corent);
113
+ /** Text to speech; synchronous, returns the finished audio and exact charge. */
114
+ generate(text: string, options?: {
115
+ voiceId?: string;
116
+ }): Promise<GeneratedSpeech>;
117
+ }
118
+ declare class Jobs {
119
+ private c;
120
+ constructor(c: Corent);
121
+ get(jobId: string): Promise<Job>;
122
+ wait(jobId: string, timeoutMs?: number): Promise<Job>;
123
+ }
124
+
125
+ export { Corent, CorentError, type GeneratedImage, type GeneratedSpeech, type GeneratedVideo, GenerationFailedError, InsufficientBalanceError, InvalidRequestError, type Job, RateLimitedError, type Tier };
package/dist/index.js ADDED
@@ -0,0 +1,231 @@
1
+ // src/index.ts
2
+ var DEFAULT_BASE_URL = "https://api.corent.tech";
3
+ var MAX_RETRIES = 3;
4
+ var POLL_INTERVAL_MS = 3e3;
5
+ var DEFAULT_WAIT_TIMEOUT_MS = 9e5;
6
+ var CorentError = class extends Error {
7
+ constructor(message, statusCode) {
8
+ super(message);
9
+ this.statusCode = statusCode;
10
+ this.name = "CorentError";
11
+ }
12
+ statusCode;
13
+ };
14
+ var InvalidRequestError = class extends CorentError {
15
+ name = "InvalidRequestError";
16
+ };
17
+ var InsufficientBalanceError = class extends CorentError {
18
+ name = "InsufficientBalanceError";
19
+ };
20
+ var RateLimitedError = class extends CorentError {
21
+ name = "RateLimitedError";
22
+ };
23
+ var GenerationFailedError = class extends CorentError {
24
+ name = "GenerationFailedError";
25
+ };
26
+ var Corent = class {
27
+ baseUrl;
28
+ apiKey;
29
+ fetchFn;
30
+ images;
31
+ videos;
32
+ speech;
33
+ jobs;
34
+ constructor(apiKey, options = {}) {
35
+ this.apiKey = apiKey;
36
+ this.baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;
37
+ this.fetchFn = options.fetch ?? fetch;
38
+ this.images = new Images(this);
39
+ this.videos = new Videos(this);
40
+ this.speech = new Speech(this);
41
+ this.jobs = new Jobs(this);
42
+ }
43
+ /** Prepaid balance in cents. */
44
+ async balanceCents() {
45
+ const r = await this.request("GET", "/v1/account/balance");
46
+ return r.balance_cents;
47
+ }
48
+ /** The live tier catalog with honest min–max price ranges. Public. */
49
+ async tiers() {
50
+ const r = await this.request("GET", "/v1/tiers");
51
+ return r.tiers;
52
+ }
53
+ /** @internal */
54
+ async request(method, path, body, idempotent = false) {
55
+ const headers = {
56
+ Authorization: `Bearer ${this.apiKey}`,
57
+ "User-Agent": "corent-js/0.1.0"
58
+ };
59
+ if (body) headers["Content-Type"] = "application/json";
60
+ if (idempotent) headers["Idempotency-Key"] = crypto.randomUUID();
61
+ let lastError;
62
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
63
+ let resp;
64
+ try {
65
+ resp = await this.fetchFn(`${this.baseUrl}${path}`, {
66
+ method,
67
+ headers,
68
+ body: body ? JSON.stringify(body) : void 0
69
+ });
70
+ } catch (err) {
71
+ lastError = err;
72
+ await sleep(Math.min(2 ** attempt * 1e3, 8e3));
73
+ continue;
74
+ }
75
+ if (resp.status < 400) return await resp.json();
76
+ if (resp.status === 429) {
77
+ const retryAfter = Number(resp.headers.get("Retry-After") ?? 2 ** (attempt + 1));
78
+ if (attempt < MAX_RETRIES) {
79
+ await sleep(Math.min(retryAfter * 1e3, 3e4));
80
+ continue;
81
+ }
82
+ throw new RateLimitedError("rate limited", 429);
83
+ }
84
+ if ((resp.status === 502 || resp.status === 503) && attempt < MAX_RETRIES) {
85
+ await sleep(Math.min(2 ** (attempt + 1) * 1e3, 8e3));
86
+ continue;
87
+ }
88
+ throw await errorFor(resp);
89
+ }
90
+ throw new CorentError(`request failed after ${MAX_RETRIES} retries: ${lastError}`);
91
+ }
92
+ /** @internal */
93
+ async waitForJob(jobId, timeoutMs) {
94
+ const deadline = Date.now() + timeoutMs;
95
+ while (Date.now() < deadline) {
96
+ const job = await this.request("GET", `/v1/jobs/${jobId}`);
97
+ if (job.status === "completed") return job;
98
+ if (job.status === "failed") {
99
+ throw new GenerationFailedError(job.error ?? "generation_failed");
100
+ }
101
+ await sleep(POLL_INTERVAL_MS);
102
+ }
103
+ throw new CorentError(
104
+ `job ${jobId} still processing after ${Math.round(timeoutMs / 1e3)}s; poll client.jobs.get("${jobId}") to resume`
105
+ );
106
+ }
107
+ };
108
+ var Images = class {
109
+ constructor(c) {
110
+ this.c = c;
111
+ }
112
+ c;
113
+ /** Render an image. Submits as a background job and polls, so a client
114
+ * timeout can never lose a finished render. Pass wait:false for the Job. */
115
+ async generate(prompt, options = {}) {
116
+ const body = {
117
+ prompt,
118
+ aspect_ratio: options.aspectRatio ?? "1:1",
119
+ async: true
120
+ };
121
+ if (options.tier) body.tier = options.tier;
122
+ if (options.style) body.style = options.style;
123
+ const submitted = await this.c.request("POST", "/v1/images/generate", body, true);
124
+ if (submitted.status === "completed") return imageFromJob(submitted);
125
+ if (options.wait === false) {
126
+ return { id: submitted.id, status: submitted.status, raw: submitted };
127
+ }
128
+ return imageFromJob(await this.c.waitForJob(submitted.id, options.timeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS));
129
+ }
130
+ };
131
+ var Videos = class {
132
+ constructor(c) {
133
+ this.c = c;
134
+ }
135
+ c;
136
+ /** Render a video (1–5 minutes typical). resolution: 720p | 1080p | 4k —
137
+ * tier-capped, clamped down rather than rejected. imageUrl animates an
138
+ * existing image (image-to-video). */
139
+ async generate(prompt, options = {}) {
140
+ const body = {
141
+ prompt,
142
+ aspect_ratio: options.aspectRatio ?? "16:9"
143
+ };
144
+ if (options.tier) body.tier = options.tier;
145
+ if (options.durationS !== void 0) body.duration_s = options.durationS;
146
+ if (options.resolution) body.resolution = options.resolution;
147
+ if (options.imageUrl) body.image_url = options.imageUrl;
148
+ const submitted = await this.c.request("POST", "/v1/videos/generate", body, true);
149
+ if (options.wait === false) {
150
+ return { id: submitted.id, status: submitted.status, raw: submitted };
151
+ }
152
+ return videoFromJob(await this.c.waitForJob(submitted.id, options.timeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS));
153
+ }
154
+ };
155
+ var Speech = class {
156
+ constructor(c) {
157
+ this.c = c;
158
+ }
159
+ c;
160
+ /** Text to speech; synchronous, returns the finished audio and exact charge. */
161
+ async generate(text, options = {}) {
162
+ const body = { text };
163
+ if (options.voiceId) body.voice_id = options.voiceId;
164
+ const r = await this.c.request("POST", "/v1/audio/speech", body, true);
165
+ const meta = r.meta ?? {};
166
+ return { id: r.id, url: r.audio_url, model: meta.model, costCents: meta.cost_cents };
167
+ }
168
+ };
169
+ var Jobs = class {
170
+ constructor(c) {
171
+ this.c = c;
172
+ }
173
+ c;
174
+ async get(jobId) {
175
+ const raw = await this.c.request("GET", `/v1/jobs/${jobId}`);
176
+ return { id: raw.id, status: raw.status, raw };
177
+ }
178
+ async wait(jobId, timeoutMs = DEFAULT_WAIT_TIMEOUT_MS) {
179
+ const raw = await this.c.waitForJob(jobId, timeoutMs);
180
+ return { id: raw.id, status: raw.status, raw };
181
+ }
182
+ };
183
+ async function errorFor(resp) {
184
+ let message;
185
+ try {
186
+ const parsed = await resp.json();
187
+ message = typeof parsed.detail === "string" ? parsed.detail : JSON.stringify(parsed.detail ?? parsed);
188
+ } catch {
189
+ message = `HTTP ${resp.status}`;
190
+ }
191
+ if (resp.status === 402) return new InsufficientBalanceError(message, 402);
192
+ if (resp.status === 400 || resp.status === 422) return new InvalidRequestError(message, resp.status);
193
+ return new CorentError(message, resp.status);
194
+ }
195
+ function imageFromJob(job) {
196
+ const image = (job.images ?? [{}])[0];
197
+ const meta = job.meta ?? {};
198
+ return {
199
+ id: job.id,
200
+ url: image.url,
201
+ width: image.width,
202
+ height: image.height,
203
+ model: meta.model,
204
+ costCents: meta.cost_cents
205
+ };
206
+ }
207
+ function videoFromJob(job) {
208
+ const video = (job.videos ?? [{}])[0];
209
+ const meta = job.meta ?? {};
210
+ return {
211
+ id: job.id,
212
+ url: video.url,
213
+ width: video.width,
214
+ height: video.height,
215
+ resolution: video.resolution,
216
+ durationS: video.duration_s,
217
+ model: meta.model,
218
+ costCents: meta.cost_cents
219
+ };
220
+ }
221
+ function sleep(ms) {
222
+ return new Promise((resolve) => setTimeout(resolve, ms));
223
+ }
224
+ export {
225
+ Corent,
226
+ CorentError,
227
+ GenerationFailedError,
228
+ InsufficientBalanceError,
229
+ InvalidRequestError,
230
+ RateLimitedError
231
+ };
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "corent-sdk",
3
+ "version": "0.1.0",
4
+ "description": "Official TypeScript/JavaScript SDK for Corent — one API for AI image, video, and voice generation with built-in routing, failover, and exact receipts.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ }
16
+ },
17
+ "files": ["dist", "README.md"],
18
+ "engines": { "node": ">=18" },
19
+ "scripts": {
20
+ "build": "tsup src/index.ts --format esm,cjs --dts --clean",
21
+ "test": "npm run build && node --test test/*.test.js"
22
+ },
23
+ "keywords": ["ai", "image-generation", "video-generation", "text-to-speech", "api", "corent"],
24
+ "homepage": "https://corent.tech/docs",
25
+ "devDependencies": {
26
+ "tsup": "^8.0.0",
27
+ "typescript": "^5.4.0"
28
+ }
29
+ }