creacortex 0.2.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Creacortex
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # creacortex
2
+
3
+ Node / TypeScript client for the **Creacortex** video-creative attention-analysis API.
4
+
5
+ Upload a video creative, run it through the pipeline, and pull back per-second
6
+ attention data, a Gen-AI optimization payload, and PDF reports. Works in Node 18+
7
+ (global `fetch`) and modern browsers.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install creacortex
13
+ ```
14
+
15
+ ## Quick start
16
+
17
+ ```ts
18
+ import { Creacortex } from "creacortex";
19
+
20
+ const cx = new Creacortex("ace_your_api_key");
21
+
22
+ // one call: upload + start + wait for completion
23
+ const run = await cx.analyze("ad.mp4", { withAudio: true, creativeId: "summer-promo" });
24
+
25
+ const csv = await cx.getCsv(run.runId); // Uint8Array — per-second analysis
26
+ const payload = await cx.getExport(run.runId); // Gen-AI insights (JSON)
27
+ const pdf = await cx.getReport(run.runId, "diagnose"); // Uint8Array — PDF report
28
+ ```
29
+
30
+ Get an API key from **Usage & billing → API keys** in the dashboard. It is sent as
31
+ the `X-API-Key` header.
32
+
33
+ `analyze` / `uploadVideo` accept a file path (Node), a `Blob`/`File` (browser), or raw
34
+ bytes (`Uint8Array` / `ArrayBuffer`).
35
+
36
+ ## Calibration
37
+
38
+ Improve prediction accuracy (and unlock all metrics) by calibrating on your real performance data.
39
+ Any CSV works — map your columns to our fields:
40
+
41
+ ```ts
42
+ const csv = await readFile("performance.csv");
43
+ const a = (await cx.calibrationAnalyze(csv)) as { guess: Record<string, string> };
44
+ const mapping = { ...a.guess, creative_id: "Ad name" }; // adjust if needed
45
+ await cx.calibrationPreview(csv, mapping, 50000); // cost + impression split
46
+ await cx.calibrationCommit(csv, mapping, { processUnmatched: true }); // charge + fit
47
+
48
+ (await cx.getAccount()).calibrated; // true once fitted
49
+ ```
50
+
51
+ ## Step by step
52
+
53
+ ```ts
54
+ const videoId = await cx.uploadVideo("ad.mp4");
55
+ let run = await cx.createRun(videoId, { withAudio: true, audience: "men 20-45, mobile gamers" });
56
+ run = await cx.wait(run.runId); // poll until done (or throws RunFailedError / WaitTimeout)
57
+
58
+ for (const item of await cx.listRuns()) console.log(item.runId, item.status, item.ready);
59
+ console.log((await cx.getAccount()).balance); // credits left
60
+ ```
61
+
62
+ ## Errors
63
+
64
+ | Class | When |
65
+ |---|---|
66
+ | `AuthError` | invalid/missing API key (401) |
67
+ | `InsufficientCreditsError` | not enough credits (402) |
68
+ | `RateLimitError` | too many requests (429) |
69
+ | `NotFoundError` | unknown run (404) |
70
+ | `RunFailedError` | the run finished with `status: "failed"` |
71
+ | `WaitTimeout` | `wait()` exceeded its timeout |
72
+ | `ApiError` | any other HTTP error (has `.status`) |
73
+
74
+ All extend `CreacortexError`.
75
+
76
+ ## Options
77
+
78
+ ```ts
79
+ new Creacortex("ace_...", {
80
+ baseUrl: "https://api.creacortex.ai",
81
+ timeoutMs: 60_000,
82
+ fetch: customFetch, // optional: inject a fetch implementation
83
+ });
84
+ ```
85
+
86
+ ## License
87
+
88
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,309 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ ApiError: () => ApiError,
34
+ AuthError: () => AuthError,
35
+ Creacortex: () => Creacortex,
36
+ CreacortexError: () => CreacortexError,
37
+ DEFAULT_BASE_URL: () => DEFAULT_BASE_URL,
38
+ InsufficientCreditsError: () => InsufficientCreditsError,
39
+ NotFoundError: () => NotFoundError,
40
+ RateLimitError: () => RateLimitError,
41
+ RunFailedError: () => RunFailedError,
42
+ WaitTimeout: () => WaitTimeout
43
+ });
44
+ module.exports = __toCommonJS(index_exports);
45
+
46
+ // src/errors.ts
47
+ var CreacortexError = class extends Error {
48
+ constructor(message) {
49
+ super(message);
50
+ this.name = "CreacortexError";
51
+ }
52
+ };
53
+ var ApiError = class extends CreacortexError {
54
+ status;
55
+ constructor(status, message) {
56
+ super(`[${status}] ${message}`);
57
+ this.name = "ApiError";
58
+ this.status = status;
59
+ }
60
+ };
61
+ var AuthError = class extends ApiError {
62
+ constructor(status, message) {
63
+ super(status, message);
64
+ this.name = "AuthError";
65
+ }
66
+ };
67
+ var InsufficientCreditsError = class extends ApiError {
68
+ constructor(status, message) {
69
+ super(status, message);
70
+ this.name = "InsufficientCreditsError";
71
+ }
72
+ };
73
+ var RateLimitError = class extends ApiError {
74
+ constructor(status, message) {
75
+ super(status, message);
76
+ this.name = "RateLimitError";
77
+ }
78
+ };
79
+ var NotFoundError = class extends ApiError {
80
+ constructor(status, message) {
81
+ super(status, message);
82
+ this.name = "NotFoundError";
83
+ }
84
+ };
85
+ var RunFailedError = class extends CreacortexError {
86
+ runId;
87
+ cause;
88
+ constructor(runId, cause) {
89
+ super(`run ${runId} failed: ${cause ?? "unknown error"}`);
90
+ this.name = "RunFailedError";
91
+ this.runId = runId;
92
+ this.cause = cause;
93
+ }
94
+ };
95
+ var WaitTimeout = class extends CreacortexError {
96
+ constructor(message) {
97
+ super(message);
98
+ this.name = "WaitTimeout";
99
+ }
100
+ };
101
+ var BY_STATUS = {
102
+ 401: AuthError,
103
+ 402: InsufficientCreditsError,
104
+ 404: NotFoundError,
105
+ 429: RateLimitError
106
+ };
107
+ async function raiseForStatus(res) {
108
+ if (res.ok) return;
109
+ const detail = await readDetail(res);
110
+ const Cls = BY_STATUS[res.status] ?? ApiError;
111
+ throw new Cls(res.status, detail);
112
+ }
113
+ async function readDetail(res) {
114
+ const text = await res.text();
115
+ try {
116
+ const body = JSON.parse(text);
117
+ if (body && typeof body === "object" && "detail" in body) return String(body.detail);
118
+ return text;
119
+ } catch {
120
+ return text || res.statusText;
121
+ }
122
+ }
123
+
124
+ // src/models.ts
125
+ function runFromApi(d) {
126
+ return {
127
+ runId: String(d.run_id),
128
+ status: d.status,
129
+ creativeId: d.creative_id ?? null,
130
+ withAudio: Boolean(d.with_audio),
131
+ error: d.error ?? null,
132
+ ready: Boolean(d.ready),
133
+ csvUrl: d.csv_url ?? null,
134
+ exportUrl: d.export_url ?? null,
135
+ diagnosePdfUrl: d.diagnose_pdf_url ?? null,
136
+ analyzePdfUrl: d.analyze_pdf_url ?? null
137
+ };
138
+ }
139
+ function runListItemFromApi(d) {
140
+ return {
141
+ runId: String(d.run_id),
142
+ status: d.status,
143
+ creativeId: d.creative_id ?? null,
144
+ withAudio: Boolean(d.with_audio),
145
+ ready: Boolean(d.ready),
146
+ createdAt: String(d.created_at)
147
+ };
148
+ }
149
+ function accountFromApi(d) {
150
+ return {
151
+ project: d.project ?? null,
152
+ balance: Number(d.balance),
153
+ quota: Number(d.quota),
154
+ used: Number(d.used ?? 0),
155
+ calibrated: Boolean(d.calibrated)
156
+ };
157
+ }
158
+
159
+ // src/client.ts
160
+ var DEFAULT_BASE_URL = "https://api.creacortex.ai";
161
+ var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
162
+ async function toBlob(file, contentType) {
163
+ if (typeof file === "string") {
164
+ const { readFile } = await import("fs/promises");
165
+ const { basename } = await import("path");
166
+ const buf = await readFile(file);
167
+ return { blob: new Blob([buf], { type: contentType }), name: basename(file) };
168
+ }
169
+ if (file instanceof Blob) {
170
+ const named = file;
171
+ return { blob: file, name: named.name ?? "video.mp4" };
172
+ }
173
+ return { blob: new Blob([file], { type: contentType }), name: "video.mp4" };
174
+ }
175
+ var Creacortex = class {
176
+ key;
177
+ base;
178
+ timeoutMs;
179
+ fetchImpl;
180
+ constructor(apiKey, opts = {}) {
181
+ if (!apiKey) throw new Error("apiKey is required");
182
+ this.key = apiKey;
183
+ this.base = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
184
+ this.timeoutMs = opts.timeoutMs ?? 6e4;
185
+ const f = opts.fetch ?? globalThis.fetch;
186
+ if (!f) throw new Error("global fetch is unavailable \u2014 pass options.fetch");
187
+ this.fetchImpl = f;
188
+ }
189
+ async req(path, init = {}) {
190
+ const ctrl = new AbortController();
191
+ const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
192
+ try {
193
+ const headers = { "X-API-Key": this.key, ...init.headers };
194
+ return await this.fetchImpl(this.base + path, { ...init, headers, signal: ctrl.signal });
195
+ } finally {
196
+ clearTimeout(timer);
197
+ }
198
+ }
199
+ async json(res) {
200
+ await raiseForStatus(res);
201
+ return await res.json();
202
+ }
203
+ async bytes(res) {
204
+ await raiseForStatus(res);
205
+ return new Uint8Array(await res.arrayBuffer());
206
+ }
207
+ async uploadVideo(file, opts = {}) {
208
+ const { blob, name } = await toBlob(file, opts.contentType ?? "video/mp4");
209
+ const form = new FormData();
210
+ form.append("file", blob, opts.filename ?? name);
211
+ const res = await this.req("/v1/videos", { method: "POST", body: form });
212
+ return String((await this.json(res)).video_id);
213
+ }
214
+ async createRun(videoId, opts = {}) {
215
+ const body = JSON.stringify({
216
+ video_id: videoId,
217
+ creative_id: opts.creativeId ?? null,
218
+ with_audio: opts.withAudio ?? false,
219
+ audience: opts.audience ?? null
220
+ });
221
+ const res = await this.req("/v1/runs", {
222
+ method: "POST",
223
+ headers: { "content-type": "application/json" },
224
+ body
225
+ });
226
+ return runFromApi(await this.json(res));
227
+ }
228
+ async getRun(runId) {
229
+ return runFromApi(await this.json(await this.req(`/v1/runs/${runId}`)));
230
+ }
231
+ async listRuns() {
232
+ const arr = await this.json(await this.req("/v1/runs"));
233
+ return arr.map(runListItemFromApi);
234
+ }
235
+ async getAccount() {
236
+ return accountFromApi(await this.json(await this.req("/v1/account")));
237
+ }
238
+ async calibForm(file, filename) {
239
+ const { blob, name } = await toBlob(file, "text/csv");
240
+ const form = new FormData();
241
+ form.append("file", blob, filename ?? name);
242
+ return form;
243
+ }
244
+ /** Step 1: detect columns and a guessed mapping for a performance CSV. */
245
+ async calibrationAnalyze(file, filename = "data.csv") {
246
+ const form = await this.calibForm(file, filename);
247
+ return this.json(await this.req("/v1/calibration/analyze", { method: "POST", body: form }));
248
+ }
249
+ /** Step 2: summary (impression split, matched/processable, cost) — no charge. */
250
+ async calibrationPreview(file, mapping, minImpressions = 0, filename = "data.csv") {
251
+ const form = await this.calibForm(file, filename);
252
+ form.append("mapping", JSON.stringify(mapping));
253
+ form.append("min_impressions", String(minImpressions));
254
+ return this.json(await this.req("/v1/calibration/preview", { method: "POST", body: form }));
255
+ }
256
+ /** Step 3: charge, store actuals, fit calibration, queue unmatched videos. */
257
+ async calibrationCommit(file, mapping, opts = {}) {
258
+ const form = await this.calibForm(file, opts.filename ?? "data.csv");
259
+ form.append("mapping", JSON.stringify(mapping));
260
+ form.append("min_impressions", String(opts.minImpressions ?? 0));
261
+ form.append("process_unmatched", String(opts.processUnmatched ?? false));
262
+ return this.json(await this.req("/v1/calibration/commit", { method: "POST", body: form }));
263
+ }
264
+ /** Per-second analysis CSV (raw bytes). */
265
+ async getCsv(runId) {
266
+ return this.bytes(await this.req(`/v1/runs/${runId}/data.csv`));
267
+ }
268
+ /** Gen-AI payload (parsed JSON). */
269
+ async getExport(runId) {
270
+ return this.json(await this.req(`/v1/runs/${runId}/export.json`));
271
+ }
272
+ /** PDF report bytes. kind is "diagnose" or "analyze". */
273
+ async getReport(runId, kind = "diagnose") {
274
+ return this.bytes(await this.req(`/v1/runs/${runId}/report.pdf?kind=${encodeURIComponent(kind)}`));
275
+ }
276
+ /** Poll until the run reaches a terminal state. Rejects on failure/timeout. */
277
+ async wait(runId, opts = {}) {
278
+ const intervalMs = opts.intervalMs ?? 5e3;
279
+ const deadline = Date.now() + (opts.timeoutMs ?? 18e5);
280
+ for (; ; ) {
281
+ const run = await this.getRun(runId);
282
+ if (run.status === "failed") throw new RunFailedError(run.runId, run.error ?? void 0);
283
+ if (run.status === "done") return run;
284
+ if (Date.now() >= deadline) throw new WaitTimeout(`run ${runId} not done in time`);
285
+ await sleep(intervalMs);
286
+ }
287
+ }
288
+ /** Upload, start a run, and (by default) wait for it to finish. */
289
+ async analyze(file, opts = {}) {
290
+ const videoId = await this.uploadVideo(file, { filename: opts.filename });
291
+ const run = await this.createRun(videoId, opts);
292
+ if (opts.wait === false) return run;
293
+ return this.wait(run.runId, opts);
294
+ }
295
+ };
296
+ // Annotate the CommonJS export names for ESM import in node:
297
+ 0 && (module.exports = {
298
+ ApiError,
299
+ AuthError,
300
+ Creacortex,
301
+ CreacortexError,
302
+ DEFAULT_BASE_URL,
303
+ InsufficientCreditsError,
304
+ NotFoundError,
305
+ RateLimitError,
306
+ RunFailedError,
307
+ WaitTimeout
308
+ });
309
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/models.ts","../src/client.ts"],"sourcesContent":["export { Creacortex, DEFAULT_BASE_URL } from \"./client\";\nexport type {\n AnalyzeOptions,\n ClientOptions,\n CreateRunOptions,\n FileInput,\n UploadOptions,\n WaitOptions,\n} from \"./client\";\nexport type { Account, Run, RunListItem, RunStatus } from \"./models\";\nexport {\n ApiError,\n AuthError,\n CreacortexError,\n InsufficientCreditsError,\n NotFoundError,\n RateLimitError,\n RunFailedError,\n WaitTimeout,\n} from \"./errors\";\n","/** Errors thrown by the Creacortex client. */\n\nexport class CreacortexError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"CreacortexError\";\n }\n}\n\n/** An HTTP error returned by the API (status >= 400). */\nexport class ApiError extends CreacortexError {\n readonly status: number;\n\n constructor(status: number, message: string) {\n super(`[${status}] ${message}`);\n this.name = \"ApiError\";\n this.status = status;\n }\n}\n\n/** Invalid or missing API key (401). */\nexport class AuthError extends ApiError {\n constructor(status: number, message: string) {\n super(status, message);\n this.name = \"AuthError\";\n }\n}\n\n/** Not enough credits to run the analysis (402). */\nexport class InsufficientCreditsError extends ApiError {\n constructor(status: number, message: string) {\n super(status, message);\n this.name = \"InsufficientCreditsError\";\n }\n}\n\n/** Too many requests (429). */\nexport class RateLimitError extends ApiError {\n constructor(status: number, message: string) {\n super(status, message);\n this.name = \"RateLimitError\";\n }\n}\n\n/** Unknown run or resource (404). */\nexport class NotFoundError extends ApiError {\n constructor(status: number, message: string) {\n super(status, message);\n this.name = \"NotFoundError\";\n }\n}\n\n/** The run finished with status=\"failed\". */\nexport class RunFailedError extends CreacortexError {\n readonly runId: string;\n readonly cause?: string;\n\n constructor(runId: string, cause?: string) {\n super(`run ${runId} failed: ${cause ?? \"unknown error\"}`);\n this.name = \"RunFailedError\";\n this.runId = runId;\n this.cause = cause;\n }\n}\n\n/** A run did not finish within the wait timeout. */\nexport class WaitTimeout extends CreacortexError {\n constructor(message: string) {\n super(message);\n this.name = \"WaitTimeout\";\n }\n}\n\nconst BY_STATUS: Record<number, new (s: number, m: string) => ApiError> = {\n 401: AuthError,\n 402: InsufficientCreditsError,\n 404: NotFoundError,\n 429: RateLimitError,\n};\n\n/** Throw the appropriate typed error for a non-OK response. */\nexport async function raiseForStatus(res: Response): Promise<void> {\n if (res.ok) return;\n const detail = await readDetail(res);\n const Cls = BY_STATUS[res.status] ?? ApiError;\n throw new Cls(res.status, detail);\n}\n\nasync function readDetail(res: Response): Promise<string> {\n const text = await res.text();\n try {\n const body = JSON.parse(text);\n if (body && typeof body === \"object\" && \"detail\" in body) return String(body.detail);\n return text;\n } catch {\n return text || res.statusText;\n }\n}\n","/** Typed response models (camelCase), mapped from the API's snake_case JSON. */\n\nexport type RunStatus = \"queued\" | \"running\" | \"done\" | \"failed\";\n\nexport interface Run {\n runId: string;\n status: RunStatus;\n creativeId?: string | null;\n withAudio: boolean;\n error?: string | null;\n ready: boolean;\n csvUrl?: string | null;\n exportUrl?: string | null;\n diagnosePdfUrl?: string | null;\n analyzePdfUrl?: string | null;\n}\n\nexport interface RunListItem {\n runId: string;\n status: RunStatus;\n creativeId?: string | null;\n withAudio: boolean;\n ready: boolean;\n createdAt: string;\n}\n\nexport interface Account {\n project?: string | null;\n balance: number;\n quota: number;\n used: number;\n calibrated: boolean;\n}\n\ntype Json = Record<string, unknown>;\n\nexport function runFromApi(d: Json): Run {\n return {\n runId: String(d.run_id),\n status: d.status as RunStatus,\n creativeId: (d.creative_id as string | null) ?? null,\n withAudio: Boolean(d.with_audio),\n error: (d.error as string | null) ?? null,\n ready: Boolean(d.ready),\n csvUrl: (d.csv_url as string | null) ?? null,\n exportUrl: (d.export_url as string | null) ?? null,\n diagnosePdfUrl: (d.diagnose_pdf_url as string | null) ?? null,\n analyzePdfUrl: (d.analyze_pdf_url as string | null) ?? null,\n };\n}\n\nexport function runListItemFromApi(d: Json): RunListItem {\n return {\n runId: String(d.run_id),\n status: d.status as RunStatus,\n creativeId: (d.creative_id as string | null) ?? null,\n withAudio: Boolean(d.with_audio),\n ready: Boolean(d.ready),\n createdAt: String(d.created_at),\n };\n}\n\nexport function accountFromApi(d: Json): Account {\n return {\n project: (d.project as string | null) ?? null,\n balance: Number(d.balance),\n quota: Number(d.quota),\n used: Number(d.used ?? 0),\n calibrated: Boolean(d.calibrated),\n };\n}\n\nexport const isTerminal = (s: RunStatus): boolean => s === \"done\" || s === \"failed\";\n","import { RunFailedError, WaitTimeout, raiseForStatus } from \"./errors\";\nimport {\n Account,\n Run,\n RunListItem,\n accountFromApi,\n runFromApi,\n runListItemFromApi,\n} from \"./models\";\n\nexport const DEFAULT_BASE_URL = \"https://api.creacortex.ai\";\n\n/** A video to upload: a Blob/File, raw bytes, or (in Node) a file path. */\nexport type FileInput = Blob | Uint8Array | ArrayBuffer | string;\n\nexport interface ClientOptions {\n baseUrl?: string;\n timeoutMs?: number;\n /** Inject a fetch implementation (tests, custom agents). Defaults to global fetch. */\n fetch?: typeof fetch;\n}\n\nexport interface UploadOptions {\n filename?: string;\n contentType?: string;\n}\n\nexport interface CreateRunOptions {\n creativeId?: string;\n withAudio?: boolean;\n audience?: string;\n}\n\nexport interface WaitOptions {\n intervalMs?: number;\n timeoutMs?: number;\n}\n\nexport interface AnalyzeOptions extends CreateRunOptions, WaitOptions {\n filename?: string;\n /** Wait for the run to finish before resolving. Default: true. */\n wait?: boolean;\n}\n\nconst sleep = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms));\n\nasync function toBlob(file: FileInput, contentType: string): Promise<{ blob: Blob; name: string }> {\n if (typeof file === \"string\") {\n const { readFile } = await import(\"node:fs/promises\");\n const { basename } = await import(\"node:path\");\n const buf = await readFile(file);\n return { blob: new Blob([buf], { type: contentType }), name: basename(file) };\n }\n if (file instanceof Blob) {\n const named = file as Blob & { name?: string };\n return { blob: file, name: named.name ?? \"video.mp4\" };\n }\n return { blob: new Blob([file], { type: contentType }), name: \"video.mp4\" };\n}\n\nexport class Creacortex {\n private readonly key: string;\n private readonly base: string;\n private readonly timeoutMs: number;\n private readonly fetchImpl: typeof fetch;\n\n constructor(apiKey: string, opts: ClientOptions = {}) {\n if (!apiKey) throw new Error(\"apiKey is required\");\n this.key = apiKey;\n this.base = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.timeoutMs = opts.timeoutMs ?? 60_000;\n const f = opts.fetch ?? globalThis.fetch;\n if (!f) throw new Error(\"global fetch is unavailable — pass options.fetch\");\n this.fetchImpl = f;\n }\n\n private async req(path: string, init: RequestInit = {}): Promise<Response> {\n const ctrl = new AbortController();\n const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);\n try {\n const headers = { \"X-API-Key\": this.key, ...(init.headers as Record<string, string>) };\n return await this.fetchImpl(this.base + path, { ...init, headers, signal: ctrl.signal });\n } finally {\n clearTimeout(timer);\n }\n }\n\n private async json<T>(res: Response): Promise<T> {\n await raiseForStatus(res);\n return (await res.json()) as T;\n }\n\n private async bytes(res: Response): Promise<Uint8Array> {\n await raiseForStatus(res);\n return new Uint8Array(await res.arrayBuffer());\n }\n\n async uploadVideo(file: FileInput, opts: UploadOptions = {}): Promise<string> {\n const { blob, name } = await toBlob(file, opts.contentType ?? \"video/mp4\");\n const form = new FormData();\n form.append(\"file\", blob, opts.filename ?? name);\n const res = await this.req(\"/v1/videos\", { method: \"POST\", body: form });\n return String((await this.json<{ video_id: string }>(res)).video_id);\n }\n\n async createRun(videoId: string, opts: CreateRunOptions = {}): Promise<Run> {\n const body = JSON.stringify({\n video_id: videoId,\n creative_id: opts.creativeId ?? null,\n with_audio: opts.withAudio ?? false,\n audience: opts.audience ?? null,\n });\n const res = await this.req(\"/v1/runs\", {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body,\n });\n return runFromApi(await this.json(res));\n }\n\n async getRun(runId: string): Promise<Run> {\n return runFromApi(await this.json(await this.req(`/v1/runs/${runId}`)));\n }\n\n async listRuns(): Promise<RunListItem[]> {\n const arr = await this.json<Record<string, unknown>[]>(await this.req(\"/v1/runs\"));\n return arr.map(runListItemFromApi);\n }\n\n async getAccount(): Promise<Account> {\n return accountFromApi(await this.json(await this.req(\"/v1/account\")));\n }\n\n private async calibForm(file: FileInput, filename: string): Promise<FormData> {\n const { blob, name } = await toBlob(file, \"text/csv\");\n const form = new FormData();\n form.append(\"file\", blob, filename ?? name);\n return form;\n }\n\n /** Step 1: detect columns and a guessed mapping for a performance CSV. */\n async calibrationAnalyze(file: FileInput, filename = \"data.csv\"): Promise<unknown> {\n const form = await this.calibForm(file, filename);\n return this.json(await this.req(\"/v1/calibration/analyze\", { method: \"POST\", body: form }));\n }\n\n /** Step 2: summary (impression split, matched/processable, cost) — no charge. */\n async calibrationPreview(\n file: FileInput, mapping: Record<string, string>, minImpressions = 0, filename = \"data.csv\",\n ): Promise<unknown> {\n const form = await this.calibForm(file, filename);\n form.append(\"mapping\", JSON.stringify(mapping));\n form.append(\"min_impressions\", String(minImpressions));\n return this.json(await this.req(\"/v1/calibration/preview\", { method: \"POST\", body: form }));\n }\n\n /** Step 3: charge, store actuals, fit calibration, queue unmatched videos. */\n async calibrationCommit(\n file: FileInput, mapping: Record<string, string>,\n opts: { minImpressions?: number; processUnmatched?: boolean; filename?: string } = {},\n ): Promise<unknown> {\n const form = await this.calibForm(file, opts.filename ?? \"data.csv\");\n form.append(\"mapping\", JSON.stringify(mapping));\n form.append(\"min_impressions\", String(opts.minImpressions ?? 0));\n form.append(\"process_unmatched\", String(opts.processUnmatched ?? false));\n return this.json(await this.req(\"/v1/calibration/commit\", { method: \"POST\", body: form }));\n }\n\n /** Per-second analysis CSV (raw bytes). */\n async getCsv(runId: string): Promise<Uint8Array> {\n return this.bytes(await this.req(`/v1/runs/${runId}/data.csv`));\n }\n\n /** Gen-AI payload (parsed JSON). */\n async getExport(runId: string): Promise<unknown> {\n return this.json(await this.req(`/v1/runs/${runId}/export.json`));\n }\n\n /** PDF report bytes. kind is \"diagnose\" or \"analyze\". */\n async getReport(runId: string, kind: \"diagnose\" | \"analyze\" = \"diagnose\"): Promise<Uint8Array> {\n return this.bytes(await this.req(`/v1/runs/${runId}/report.pdf?kind=${encodeURIComponent(kind)}`));\n }\n\n /** Poll until the run reaches a terminal state. Rejects on failure/timeout. */\n async wait(runId: string, opts: WaitOptions = {}): Promise<Run> {\n const intervalMs = opts.intervalMs ?? 5_000;\n const deadline = Date.now() + (opts.timeoutMs ?? 1_800_000);\n for (;;) {\n const run = await this.getRun(runId);\n if (run.status === \"failed\") throw new RunFailedError(run.runId, run.error ?? undefined);\n if (run.status === \"done\") return run;\n if (Date.now() >= deadline) throw new WaitTimeout(`run ${runId} not done in time`);\n await sleep(intervalMs);\n }\n }\n\n /** Upload, start a run, and (by default) wait for it to finish. */\n async analyze(file: FileInput, opts: AnalyzeOptions = {}): Promise<Run> {\n const videoId = await this.uploadVideo(file, { filename: opts.filename });\n const run = await this.createRun(videoId, opts);\n if (opts.wait === false) return run;\n return this.wait(run.runId, opts);\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,131 @@
1
+ /** Typed response models (camelCase), mapped from the API's snake_case JSON. */
2
+ type RunStatus = "queued" | "running" | "done" | "failed";
3
+ interface Run {
4
+ runId: string;
5
+ status: RunStatus;
6
+ creativeId?: string | null;
7
+ withAudio: boolean;
8
+ error?: string | null;
9
+ ready: boolean;
10
+ csvUrl?: string | null;
11
+ exportUrl?: string | null;
12
+ diagnosePdfUrl?: string | null;
13
+ analyzePdfUrl?: string | null;
14
+ }
15
+ interface RunListItem {
16
+ runId: string;
17
+ status: RunStatus;
18
+ creativeId?: string | null;
19
+ withAudio: boolean;
20
+ ready: boolean;
21
+ createdAt: string;
22
+ }
23
+ interface Account {
24
+ project?: string | null;
25
+ balance: number;
26
+ quota: number;
27
+ used: number;
28
+ calibrated: boolean;
29
+ }
30
+
31
+ declare const DEFAULT_BASE_URL = "https://api.creacortex.ai";
32
+ /** A video to upload: a Blob/File, raw bytes, or (in Node) a file path. */
33
+ type FileInput = Blob | Uint8Array | ArrayBuffer | string;
34
+ interface ClientOptions {
35
+ baseUrl?: string;
36
+ timeoutMs?: number;
37
+ /** Inject a fetch implementation (tests, custom agents). Defaults to global fetch. */
38
+ fetch?: typeof fetch;
39
+ }
40
+ interface UploadOptions {
41
+ filename?: string;
42
+ contentType?: string;
43
+ }
44
+ interface CreateRunOptions {
45
+ creativeId?: string;
46
+ withAudio?: boolean;
47
+ audience?: string;
48
+ }
49
+ interface WaitOptions {
50
+ intervalMs?: number;
51
+ timeoutMs?: number;
52
+ }
53
+ interface AnalyzeOptions extends CreateRunOptions, WaitOptions {
54
+ filename?: string;
55
+ /** Wait for the run to finish before resolving. Default: true. */
56
+ wait?: boolean;
57
+ }
58
+ declare class Creacortex {
59
+ private readonly key;
60
+ private readonly base;
61
+ private readonly timeoutMs;
62
+ private readonly fetchImpl;
63
+ constructor(apiKey: string, opts?: ClientOptions);
64
+ private req;
65
+ private json;
66
+ private bytes;
67
+ uploadVideo(file: FileInput, opts?: UploadOptions): Promise<string>;
68
+ createRun(videoId: string, opts?: CreateRunOptions): Promise<Run>;
69
+ getRun(runId: string): Promise<Run>;
70
+ listRuns(): Promise<RunListItem[]>;
71
+ getAccount(): Promise<Account>;
72
+ private calibForm;
73
+ /** Step 1: detect columns and a guessed mapping for a performance CSV. */
74
+ calibrationAnalyze(file: FileInput, filename?: string): Promise<unknown>;
75
+ /** Step 2: summary (impression split, matched/processable, cost) — no charge. */
76
+ calibrationPreview(file: FileInput, mapping: Record<string, string>, minImpressions?: number, filename?: string): Promise<unknown>;
77
+ /** Step 3: charge, store actuals, fit calibration, queue unmatched videos. */
78
+ calibrationCommit(file: FileInput, mapping: Record<string, string>, opts?: {
79
+ minImpressions?: number;
80
+ processUnmatched?: boolean;
81
+ filename?: string;
82
+ }): Promise<unknown>;
83
+ /** Per-second analysis CSV (raw bytes). */
84
+ getCsv(runId: string): Promise<Uint8Array>;
85
+ /** Gen-AI payload (parsed JSON). */
86
+ getExport(runId: string): Promise<unknown>;
87
+ /** PDF report bytes. kind is "diagnose" or "analyze". */
88
+ getReport(runId: string, kind?: "diagnose" | "analyze"): Promise<Uint8Array>;
89
+ /** Poll until the run reaches a terminal state. Rejects on failure/timeout. */
90
+ wait(runId: string, opts?: WaitOptions): Promise<Run>;
91
+ /** Upload, start a run, and (by default) wait for it to finish. */
92
+ analyze(file: FileInput, opts?: AnalyzeOptions): Promise<Run>;
93
+ }
94
+
95
+ /** Errors thrown by the Creacortex client. */
96
+ declare class CreacortexError extends Error {
97
+ constructor(message: string);
98
+ }
99
+ /** An HTTP error returned by the API (status >= 400). */
100
+ declare class ApiError extends CreacortexError {
101
+ readonly status: number;
102
+ constructor(status: number, message: string);
103
+ }
104
+ /** Invalid or missing API key (401). */
105
+ declare class AuthError extends ApiError {
106
+ constructor(status: number, message: string);
107
+ }
108
+ /** Not enough credits to run the analysis (402). */
109
+ declare class InsufficientCreditsError extends ApiError {
110
+ constructor(status: number, message: string);
111
+ }
112
+ /** Too many requests (429). */
113
+ declare class RateLimitError extends ApiError {
114
+ constructor(status: number, message: string);
115
+ }
116
+ /** Unknown run or resource (404). */
117
+ declare class NotFoundError extends ApiError {
118
+ constructor(status: number, message: string);
119
+ }
120
+ /** The run finished with status="failed". */
121
+ declare class RunFailedError extends CreacortexError {
122
+ readonly runId: string;
123
+ readonly cause?: string;
124
+ constructor(runId: string, cause?: string);
125
+ }
126
+ /** A run did not finish within the wait timeout. */
127
+ declare class WaitTimeout extends CreacortexError {
128
+ constructor(message: string);
129
+ }
130
+
131
+ export { type Account, type AnalyzeOptions, ApiError, AuthError, type ClientOptions, Creacortex, CreacortexError, type CreateRunOptions, DEFAULT_BASE_URL, type FileInput, InsufficientCreditsError, NotFoundError, RateLimitError, type Run, RunFailedError, type RunListItem, type RunStatus, type UploadOptions, type WaitOptions, WaitTimeout };
@@ -0,0 +1,131 @@
1
+ /** Typed response models (camelCase), mapped from the API's snake_case JSON. */
2
+ type RunStatus = "queued" | "running" | "done" | "failed";
3
+ interface Run {
4
+ runId: string;
5
+ status: RunStatus;
6
+ creativeId?: string | null;
7
+ withAudio: boolean;
8
+ error?: string | null;
9
+ ready: boolean;
10
+ csvUrl?: string | null;
11
+ exportUrl?: string | null;
12
+ diagnosePdfUrl?: string | null;
13
+ analyzePdfUrl?: string | null;
14
+ }
15
+ interface RunListItem {
16
+ runId: string;
17
+ status: RunStatus;
18
+ creativeId?: string | null;
19
+ withAudio: boolean;
20
+ ready: boolean;
21
+ createdAt: string;
22
+ }
23
+ interface Account {
24
+ project?: string | null;
25
+ balance: number;
26
+ quota: number;
27
+ used: number;
28
+ calibrated: boolean;
29
+ }
30
+
31
+ declare const DEFAULT_BASE_URL = "https://api.creacortex.ai";
32
+ /** A video to upload: a Blob/File, raw bytes, or (in Node) a file path. */
33
+ type FileInput = Blob | Uint8Array | ArrayBuffer | string;
34
+ interface ClientOptions {
35
+ baseUrl?: string;
36
+ timeoutMs?: number;
37
+ /** Inject a fetch implementation (tests, custom agents). Defaults to global fetch. */
38
+ fetch?: typeof fetch;
39
+ }
40
+ interface UploadOptions {
41
+ filename?: string;
42
+ contentType?: string;
43
+ }
44
+ interface CreateRunOptions {
45
+ creativeId?: string;
46
+ withAudio?: boolean;
47
+ audience?: string;
48
+ }
49
+ interface WaitOptions {
50
+ intervalMs?: number;
51
+ timeoutMs?: number;
52
+ }
53
+ interface AnalyzeOptions extends CreateRunOptions, WaitOptions {
54
+ filename?: string;
55
+ /** Wait for the run to finish before resolving. Default: true. */
56
+ wait?: boolean;
57
+ }
58
+ declare class Creacortex {
59
+ private readonly key;
60
+ private readonly base;
61
+ private readonly timeoutMs;
62
+ private readonly fetchImpl;
63
+ constructor(apiKey: string, opts?: ClientOptions);
64
+ private req;
65
+ private json;
66
+ private bytes;
67
+ uploadVideo(file: FileInput, opts?: UploadOptions): Promise<string>;
68
+ createRun(videoId: string, opts?: CreateRunOptions): Promise<Run>;
69
+ getRun(runId: string): Promise<Run>;
70
+ listRuns(): Promise<RunListItem[]>;
71
+ getAccount(): Promise<Account>;
72
+ private calibForm;
73
+ /** Step 1: detect columns and a guessed mapping for a performance CSV. */
74
+ calibrationAnalyze(file: FileInput, filename?: string): Promise<unknown>;
75
+ /** Step 2: summary (impression split, matched/processable, cost) — no charge. */
76
+ calibrationPreview(file: FileInput, mapping: Record<string, string>, minImpressions?: number, filename?: string): Promise<unknown>;
77
+ /** Step 3: charge, store actuals, fit calibration, queue unmatched videos. */
78
+ calibrationCommit(file: FileInput, mapping: Record<string, string>, opts?: {
79
+ minImpressions?: number;
80
+ processUnmatched?: boolean;
81
+ filename?: string;
82
+ }): Promise<unknown>;
83
+ /** Per-second analysis CSV (raw bytes). */
84
+ getCsv(runId: string): Promise<Uint8Array>;
85
+ /** Gen-AI payload (parsed JSON). */
86
+ getExport(runId: string): Promise<unknown>;
87
+ /** PDF report bytes. kind is "diagnose" or "analyze". */
88
+ getReport(runId: string, kind?: "diagnose" | "analyze"): Promise<Uint8Array>;
89
+ /** Poll until the run reaches a terminal state. Rejects on failure/timeout. */
90
+ wait(runId: string, opts?: WaitOptions): Promise<Run>;
91
+ /** Upload, start a run, and (by default) wait for it to finish. */
92
+ analyze(file: FileInput, opts?: AnalyzeOptions): Promise<Run>;
93
+ }
94
+
95
+ /** Errors thrown by the Creacortex client. */
96
+ declare class CreacortexError extends Error {
97
+ constructor(message: string);
98
+ }
99
+ /** An HTTP error returned by the API (status >= 400). */
100
+ declare class ApiError extends CreacortexError {
101
+ readonly status: number;
102
+ constructor(status: number, message: string);
103
+ }
104
+ /** Invalid or missing API key (401). */
105
+ declare class AuthError extends ApiError {
106
+ constructor(status: number, message: string);
107
+ }
108
+ /** Not enough credits to run the analysis (402). */
109
+ declare class InsufficientCreditsError extends ApiError {
110
+ constructor(status: number, message: string);
111
+ }
112
+ /** Too many requests (429). */
113
+ declare class RateLimitError extends ApiError {
114
+ constructor(status: number, message: string);
115
+ }
116
+ /** Unknown run or resource (404). */
117
+ declare class NotFoundError extends ApiError {
118
+ constructor(status: number, message: string);
119
+ }
120
+ /** The run finished with status="failed". */
121
+ declare class RunFailedError extends CreacortexError {
122
+ readonly runId: string;
123
+ readonly cause?: string;
124
+ constructor(runId: string, cause?: string);
125
+ }
126
+ /** A run did not finish within the wait timeout. */
127
+ declare class WaitTimeout extends CreacortexError {
128
+ constructor(message: string);
129
+ }
130
+
131
+ export { type Account, type AnalyzeOptions, ApiError, AuthError, type ClientOptions, Creacortex, CreacortexError, type CreateRunOptions, DEFAULT_BASE_URL, type FileInput, InsufficientCreditsError, NotFoundError, RateLimitError, type Run, RunFailedError, type RunListItem, type RunStatus, type UploadOptions, type WaitOptions, WaitTimeout };
package/dist/index.js ADDED
@@ -0,0 +1,263 @@
1
+ // src/errors.ts
2
+ var CreacortexError = class extends Error {
3
+ constructor(message) {
4
+ super(message);
5
+ this.name = "CreacortexError";
6
+ }
7
+ };
8
+ var ApiError = class extends CreacortexError {
9
+ status;
10
+ constructor(status, message) {
11
+ super(`[${status}] ${message}`);
12
+ this.name = "ApiError";
13
+ this.status = status;
14
+ }
15
+ };
16
+ var AuthError = class extends ApiError {
17
+ constructor(status, message) {
18
+ super(status, message);
19
+ this.name = "AuthError";
20
+ }
21
+ };
22
+ var InsufficientCreditsError = class extends ApiError {
23
+ constructor(status, message) {
24
+ super(status, message);
25
+ this.name = "InsufficientCreditsError";
26
+ }
27
+ };
28
+ var RateLimitError = class extends ApiError {
29
+ constructor(status, message) {
30
+ super(status, message);
31
+ this.name = "RateLimitError";
32
+ }
33
+ };
34
+ var NotFoundError = class extends ApiError {
35
+ constructor(status, message) {
36
+ super(status, message);
37
+ this.name = "NotFoundError";
38
+ }
39
+ };
40
+ var RunFailedError = class extends CreacortexError {
41
+ runId;
42
+ cause;
43
+ constructor(runId, cause) {
44
+ super(`run ${runId} failed: ${cause ?? "unknown error"}`);
45
+ this.name = "RunFailedError";
46
+ this.runId = runId;
47
+ this.cause = cause;
48
+ }
49
+ };
50
+ var WaitTimeout = class extends CreacortexError {
51
+ constructor(message) {
52
+ super(message);
53
+ this.name = "WaitTimeout";
54
+ }
55
+ };
56
+ var BY_STATUS = {
57
+ 401: AuthError,
58
+ 402: InsufficientCreditsError,
59
+ 404: NotFoundError,
60
+ 429: RateLimitError
61
+ };
62
+ async function raiseForStatus(res) {
63
+ if (res.ok) return;
64
+ const detail = await readDetail(res);
65
+ const Cls = BY_STATUS[res.status] ?? ApiError;
66
+ throw new Cls(res.status, detail);
67
+ }
68
+ async function readDetail(res) {
69
+ const text = await res.text();
70
+ try {
71
+ const body = JSON.parse(text);
72
+ if (body && typeof body === "object" && "detail" in body) return String(body.detail);
73
+ return text;
74
+ } catch {
75
+ return text || res.statusText;
76
+ }
77
+ }
78
+
79
+ // src/models.ts
80
+ function runFromApi(d) {
81
+ return {
82
+ runId: String(d.run_id),
83
+ status: d.status,
84
+ creativeId: d.creative_id ?? null,
85
+ withAudio: Boolean(d.with_audio),
86
+ error: d.error ?? null,
87
+ ready: Boolean(d.ready),
88
+ csvUrl: d.csv_url ?? null,
89
+ exportUrl: d.export_url ?? null,
90
+ diagnosePdfUrl: d.diagnose_pdf_url ?? null,
91
+ analyzePdfUrl: d.analyze_pdf_url ?? null
92
+ };
93
+ }
94
+ function runListItemFromApi(d) {
95
+ return {
96
+ runId: String(d.run_id),
97
+ status: d.status,
98
+ creativeId: d.creative_id ?? null,
99
+ withAudio: Boolean(d.with_audio),
100
+ ready: Boolean(d.ready),
101
+ createdAt: String(d.created_at)
102
+ };
103
+ }
104
+ function accountFromApi(d) {
105
+ return {
106
+ project: d.project ?? null,
107
+ balance: Number(d.balance),
108
+ quota: Number(d.quota),
109
+ used: Number(d.used ?? 0),
110
+ calibrated: Boolean(d.calibrated)
111
+ };
112
+ }
113
+
114
+ // src/client.ts
115
+ var DEFAULT_BASE_URL = "https://api.creacortex.ai";
116
+ var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
117
+ async function toBlob(file, contentType) {
118
+ if (typeof file === "string") {
119
+ const { readFile } = await import("fs/promises");
120
+ const { basename } = await import("path");
121
+ const buf = await readFile(file);
122
+ return { blob: new Blob([buf], { type: contentType }), name: basename(file) };
123
+ }
124
+ if (file instanceof Blob) {
125
+ const named = file;
126
+ return { blob: file, name: named.name ?? "video.mp4" };
127
+ }
128
+ return { blob: new Blob([file], { type: contentType }), name: "video.mp4" };
129
+ }
130
+ var Creacortex = class {
131
+ key;
132
+ base;
133
+ timeoutMs;
134
+ fetchImpl;
135
+ constructor(apiKey, opts = {}) {
136
+ if (!apiKey) throw new Error("apiKey is required");
137
+ this.key = apiKey;
138
+ this.base = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
139
+ this.timeoutMs = opts.timeoutMs ?? 6e4;
140
+ const f = opts.fetch ?? globalThis.fetch;
141
+ if (!f) throw new Error("global fetch is unavailable \u2014 pass options.fetch");
142
+ this.fetchImpl = f;
143
+ }
144
+ async req(path, init = {}) {
145
+ const ctrl = new AbortController();
146
+ const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
147
+ try {
148
+ const headers = { "X-API-Key": this.key, ...init.headers };
149
+ return await this.fetchImpl(this.base + path, { ...init, headers, signal: ctrl.signal });
150
+ } finally {
151
+ clearTimeout(timer);
152
+ }
153
+ }
154
+ async json(res) {
155
+ await raiseForStatus(res);
156
+ return await res.json();
157
+ }
158
+ async bytes(res) {
159
+ await raiseForStatus(res);
160
+ return new Uint8Array(await res.arrayBuffer());
161
+ }
162
+ async uploadVideo(file, opts = {}) {
163
+ const { blob, name } = await toBlob(file, opts.contentType ?? "video/mp4");
164
+ const form = new FormData();
165
+ form.append("file", blob, opts.filename ?? name);
166
+ const res = await this.req("/v1/videos", { method: "POST", body: form });
167
+ return String((await this.json(res)).video_id);
168
+ }
169
+ async createRun(videoId, opts = {}) {
170
+ const body = JSON.stringify({
171
+ video_id: videoId,
172
+ creative_id: opts.creativeId ?? null,
173
+ with_audio: opts.withAudio ?? false,
174
+ audience: opts.audience ?? null
175
+ });
176
+ const res = await this.req("/v1/runs", {
177
+ method: "POST",
178
+ headers: { "content-type": "application/json" },
179
+ body
180
+ });
181
+ return runFromApi(await this.json(res));
182
+ }
183
+ async getRun(runId) {
184
+ return runFromApi(await this.json(await this.req(`/v1/runs/${runId}`)));
185
+ }
186
+ async listRuns() {
187
+ const arr = await this.json(await this.req("/v1/runs"));
188
+ return arr.map(runListItemFromApi);
189
+ }
190
+ async getAccount() {
191
+ return accountFromApi(await this.json(await this.req("/v1/account")));
192
+ }
193
+ async calibForm(file, filename) {
194
+ const { blob, name } = await toBlob(file, "text/csv");
195
+ const form = new FormData();
196
+ form.append("file", blob, filename ?? name);
197
+ return form;
198
+ }
199
+ /** Step 1: detect columns and a guessed mapping for a performance CSV. */
200
+ async calibrationAnalyze(file, filename = "data.csv") {
201
+ const form = await this.calibForm(file, filename);
202
+ return this.json(await this.req("/v1/calibration/analyze", { method: "POST", body: form }));
203
+ }
204
+ /** Step 2: summary (impression split, matched/processable, cost) — no charge. */
205
+ async calibrationPreview(file, mapping, minImpressions = 0, filename = "data.csv") {
206
+ const form = await this.calibForm(file, filename);
207
+ form.append("mapping", JSON.stringify(mapping));
208
+ form.append("min_impressions", String(minImpressions));
209
+ return this.json(await this.req("/v1/calibration/preview", { method: "POST", body: form }));
210
+ }
211
+ /** Step 3: charge, store actuals, fit calibration, queue unmatched videos. */
212
+ async calibrationCommit(file, mapping, opts = {}) {
213
+ const form = await this.calibForm(file, opts.filename ?? "data.csv");
214
+ form.append("mapping", JSON.stringify(mapping));
215
+ form.append("min_impressions", String(opts.minImpressions ?? 0));
216
+ form.append("process_unmatched", String(opts.processUnmatched ?? false));
217
+ return this.json(await this.req("/v1/calibration/commit", { method: "POST", body: form }));
218
+ }
219
+ /** Per-second analysis CSV (raw bytes). */
220
+ async getCsv(runId) {
221
+ return this.bytes(await this.req(`/v1/runs/${runId}/data.csv`));
222
+ }
223
+ /** Gen-AI payload (parsed JSON). */
224
+ async getExport(runId) {
225
+ return this.json(await this.req(`/v1/runs/${runId}/export.json`));
226
+ }
227
+ /** PDF report bytes. kind is "diagnose" or "analyze". */
228
+ async getReport(runId, kind = "diagnose") {
229
+ return this.bytes(await this.req(`/v1/runs/${runId}/report.pdf?kind=${encodeURIComponent(kind)}`));
230
+ }
231
+ /** Poll until the run reaches a terminal state. Rejects on failure/timeout. */
232
+ async wait(runId, opts = {}) {
233
+ const intervalMs = opts.intervalMs ?? 5e3;
234
+ const deadline = Date.now() + (opts.timeoutMs ?? 18e5);
235
+ for (; ; ) {
236
+ const run = await this.getRun(runId);
237
+ if (run.status === "failed") throw new RunFailedError(run.runId, run.error ?? void 0);
238
+ if (run.status === "done") return run;
239
+ if (Date.now() >= deadline) throw new WaitTimeout(`run ${runId} not done in time`);
240
+ await sleep(intervalMs);
241
+ }
242
+ }
243
+ /** Upload, start a run, and (by default) wait for it to finish. */
244
+ async analyze(file, opts = {}) {
245
+ const videoId = await this.uploadVideo(file, { filename: opts.filename });
246
+ const run = await this.createRun(videoId, opts);
247
+ if (opts.wait === false) return run;
248
+ return this.wait(run.runId, opts);
249
+ }
250
+ };
251
+ export {
252
+ ApiError,
253
+ AuthError,
254
+ Creacortex,
255
+ CreacortexError,
256
+ DEFAULT_BASE_URL,
257
+ InsufficientCreditsError,
258
+ NotFoundError,
259
+ RateLimitError,
260
+ RunFailedError,
261
+ WaitTimeout
262
+ };
263
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/models.ts","../src/client.ts"],"sourcesContent":["/** Errors thrown by the Creacortex client. */\n\nexport class CreacortexError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"CreacortexError\";\n }\n}\n\n/** An HTTP error returned by the API (status >= 400). */\nexport class ApiError extends CreacortexError {\n readonly status: number;\n\n constructor(status: number, message: string) {\n super(`[${status}] ${message}`);\n this.name = \"ApiError\";\n this.status = status;\n }\n}\n\n/** Invalid or missing API key (401). */\nexport class AuthError extends ApiError {\n constructor(status: number, message: string) {\n super(status, message);\n this.name = \"AuthError\";\n }\n}\n\n/** Not enough credits to run the analysis (402). */\nexport class InsufficientCreditsError extends ApiError {\n constructor(status: number, message: string) {\n super(status, message);\n this.name = \"InsufficientCreditsError\";\n }\n}\n\n/** Too many requests (429). */\nexport class RateLimitError extends ApiError {\n constructor(status: number, message: string) {\n super(status, message);\n this.name = \"RateLimitError\";\n }\n}\n\n/** Unknown run or resource (404). */\nexport class NotFoundError extends ApiError {\n constructor(status: number, message: string) {\n super(status, message);\n this.name = \"NotFoundError\";\n }\n}\n\n/** The run finished with status=\"failed\". */\nexport class RunFailedError extends CreacortexError {\n readonly runId: string;\n readonly cause?: string;\n\n constructor(runId: string, cause?: string) {\n super(`run ${runId} failed: ${cause ?? \"unknown error\"}`);\n this.name = \"RunFailedError\";\n this.runId = runId;\n this.cause = cause;\n }\n}\n\n/** A run did not finish within the wait timeout. */\nexport class WaitTimeout extends CreacortexError {\n constructor(message: string) {\n super(message);\n this.name = \"WaitTimeout\";\n }\n}\n\nconst BY_STATUS: Record<number, new (s: number, m: string) => ApiError> = {\n 401: AuthError,\n 402: InsufficientCreditsError,\n 404: NotFoundError,\n 429: RateLimitError,\n};\n\n/** Throw the appropriate typed error for a non-OK response. */\nexport async function raiseForStatus(res: Response): Promise<void> {\n if (res.ok) return;\n const detail = await readDetail(res);\n const Cls = BY_STATUS[res.status] ?? ApiError;\n throw new Cls(res.status, detail);\n}\n\nasync function readDetail(res: Response): Promise<string> {\n const text = await res.text();\n try {\n const body = JSON.parse(text);\n if (body && typeof body === \"object\" && \"detail\" in body) return String(body.detail);\n return text;\n } catch {\n return text || res.statusText;\n }\n}\n","/** Typed response models (camelCase), mapped from the API's snake_case JSON. */\n\nexport type RunStatus = \"queued\" | \"running\" | \"done\" | \"failed\";\n\nexport interface Run {\n runId: string;\n status: RunStatus;\n creativeId?: string | null;\n withAudio: boolean;\n error?: string | null;\n ready: boolean;\n csvUrl?: string | null;\n exportUrl?: string | null;\n diagnosePdfUrl?: string | null;\n analyzePdfUrl?: string | null;\n}\n\nexport interface RunListItem {\n runId: string;\n status: RunStatus;\n creativeId?: string | null;\n withAudio: boolean;\n ready: boolean;\n createdAt: string;\n}\n\nexport interface Account {\n project?: string | null;\n balance: number;\n quota: number;\n used: number;\n calibrated: boolean;\n}\n\ntype Json = Record<string, unknown>;\n\nexport function runFromApi(d: Json): Run {\n return {\n runId: String(d.run_id),\n status: d.status as RunStatus,\n creativeId: (d.creative_id as string | null) ?? null,\n withAudio: Boolean(d.with_audio),\n error: (d.error as string | null) ?? null,\n ready: Boolean(d.ready),\n csvUrl: (d.csv_url as string | null) ?? null,\n exportUrl: (d.export_url as string | null) ?? null,\n diagnosePdfUrl: (d.diagnose_pdf_url as string | null) ?? null,\n analyzePdfUrl: (d.analyze_pdf_url as string | null) ?? null,\n };\n}\n\nexport function runListItemFromApi(d: Json): RunListItem {\n return {\n runId: String(d.run_id),\n status: d.status as RunStatus,\n creativeId: (d.creative_id as string | null) ?? null,\n withAudio: Boolean(d.with_audio),\n ready: Boolean(d.ready),\n createdAt: String(d.created_at),\n };\n}\n\nexport function accountFromApi(d: Json): Account {\n return {\n project: (d.project as string | null) ?? null,\n balance: Number(d.balance),\n quota: Number(d.quota),\n used: Number(d.used ?? 0),\n calibrated: Boolean(d.calibrated),\n };\n}\n\nexport const isTerminal = (s: RunStatus): boolean => s === \"done\" || s === \"failed\";\n","import { RunFailedError, WaitTimeout, raiseForStatus } from \"./errors\";\nimport {\n Account,\n Run,\n RunListItem,\n accountFromApi,\n runFromApi,\n runListItemFromApi,\n} from \"./models\";\n\nexport const DEFAULT_BASE_URL = \"https://api.creacortex.ai\";\n\n/** A video to upload: a Blob/File, raw bytes, or (in Node) a file path. */\nexport type FileInput = Blob | Uint8Array | ArrayBuffer | string;\n\nexport interface ClientOptions {\n baseUrl?: string;\n timeoutMs?: number;\n /** Inject a fetch implementation (tests, custom agents). Defaults to global fetch. */\n fetch?: typeof fetch;\n}\n\nexport interface UploadOptions {\n filename?: string;\n contentType?: string;\n}\n\nexport interface CreateRunOptions {\n creativeId?: string;\n withAudio?: boolean;\n audience?: string;\n}\n\nexport interface WaitOptions {\n intervalMs?: number;\n timeoutMs?: number;\n}\n\nexport interface AnalyzeOptions extends CreateRunOptions, WaitOptions {\n filename?: string;\n /** Wait for the run to finish before resolving. Default: true. */\n wait?: boolean;\n}\n\nconst sleep = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms));\n\nasync function toBlob(file: FileInput, contentType: string): Promise<{ blob: Blob; name: string }> {\n if (typeof file === \"string\") {\n const { readFile } = await import(\"node:fs/promises\");\n const { basename } = await import(\"node:path\");\n const buf = await readFile(file);\n return { blob: new Blob([buf], { type: contentType }), name: basename(file) };\n }\n if (file instanceof Blob) {\n const named = file as Blob & { name?: string };\n return { blob: file, name: named.name ?? \"video.mp4\" };\n }\n return { blob: new Blob([file], { type: contentType }), name: \"video.mp4\" };\n}\n\nexport class Creacortex {\n private readonly key: string;\n private readonly base: string;\n private readonly timeoutMs: number;\n private readonly fetchImpl: typeof fetch;\n\n constructor(apiKey: string, opts: ClientOptions = {}) {\n if (!apiKey) throw new Error(\"apiKey is required\");\n this.key = apiKey;\n this.base = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.timeoutMs = opts.timeoutMs ?? 60_000;\n const f = opts.fetch ?? globalThis.fetch;\n if (!f) throw new Error(\"global fetch is unavailable — pass options.fetch\");\n this.fetchImpl = f;\n }\n\n private async req(path: string, init: RequestInit = {}): Promise<Response> {\n const ctrl = new AbortController();\n const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);\n try {\n const headers = { \"X-API-Key\": this.key, ...(init.headers as Record<string, string>) };\n return await this.fetchImpl(this.base + path, { ...init, headers, signal: ctrl.signal });\n } finally {\n clearTimeout(timer);\n }\n }\n\n private async json<T>(res: Response): Promise<T> {\n await raiseForStatus(res);\n return (await res.json()) as T;\n }\n\n private async bytes(res: Response): Promise<Uint8Array> {\n await raiseForStatus(res);\n return new Uint8Array(await res.arrayBuffer());\n }\n\n async uploadVideo(file: FileInput, opts: UploadOptions = {}): Promise<string> {\n const { blob, name } = await toBlob(file, opts.contentType ?? \"video/mp4\");\n const form = new FormData();\n form.append(\"file\", blob, opts.filename ?? name);\n const res = await this.req(\"/v1/videos\", { method: \"POST\", body: form });\n return String((await this.json<{ video_id: string }>(res)).video_id);\n }\n\n async createRun(videoId: string, opts: CreateRunOptions = {}): Promise<Run> {\n const body = JSON.stringify({\n video_id: videoId,\n creative_id: opts.creativeId ?? null,\n with_audio: opts.withAudio ?? false,\n audience: opts.audience ?? null,\n });\n const res = await this.req(\"/v1/runs\", {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body,\n });\n return runFromApi(await this.json(res));\n }\n\n async getRun(runId: string): Promise<Run> {\n return runFromApi(await this.json(await this.req(`/v1/runs/${runId}`)));\n }\n\n async listRuns(): Promise<RunListItem[]> {\n const arr = await this.json<Record<string, unknown>[]>(await this.req(\"/v1/runs\"));\n return arr.map(runListItemFromApi);\n }\n\n async getAccount(): Promise<Account> {\n return accountFromApi(await this.json(await this.req(\"/v1/account\")));\n }\n\n private async calibForm(file: FileInput, filename: string): Promise<FormData> {\n const { blob, name } = await toBlob(file, \"text/csv\");\n const form = new FormData();\n form.append(\"file\", blob, filename ?? name);\n return form;\n }\n\n /** Step 1: detect columns and a guessed mapping for a performance CSV. */\n async calibrationAnalyze(file: FileInput, filename = \"data.csv\"): Promise<unknown> {\n const form = await this.calibForm(file, filename);\n return this.json(await this.req(\"/v1/calibration/analyze\", { method: \"POST\", body: form }));\n }\n\n /** Step 2: summary (impression split, matched/processable, cost) — no charge. */\n async calibrationPreview(\n file: FileInput, mapping: Record<string, string>, minImpressions = 0, filename = \"data.csv\",\n ): Promise<unknown> {\n const form = await this.calibForm(file, filename);\n form.append(\"mapping\", JSON.stringify(mapping));\n form.append(\"min_impressions\", String(minImpressions));\n return this.json(await this.req(\"/v1/calibration/preview\", { method: \"POST\", body: form }));\n }\n\n /** Step 3: charge, store actuals, fit calibration, queue unmatched videos. */\n async calibrationCommit(\n file: FileInput, mapping: Record<string, string>,\n opts: { minImpressions?: number; processUnmatched?: boolean; filename?: string } = {},\n ): Promise<unknown> {\n const form = await this.calibForm(file, opts.filename ?? \"data.csv\");\n form.append(\"mapping\", JSON.stringify(mapping));\n form.append(\"min_impressions\", String(opts.minImpressions ?? 0));\n form.append(\"process_unmatched\", String(opts.processUnmatched ?? false));\n return this.json(await this.req(\"/v1/calibration/commit\", { method: \"POST\", body: form }));\n }\n\n /** Per-second analysis CSV (raw bytes). */\n async getCsv(runId: string): Promise<Uint8Array> {\n return this.bytes(await this.req(`/v1/runs/${runId}/data.csv`));\n }\n\n /** Gen-AI payload (parsed JSON). */\n async getExport(runId: string): Promise<unknown> {\n return this.json(await this.req(`/v1/runs/${runId}/export.json`));\n }\n\n /** PDF report bytes. kind is \"diagnose\" or \"analyze\". */\n async getReport(runId: string, kind: \"diagnose\" | \"analyze\" = \"diagnose\"): Promise<Uint8Array> {\n return this.bytes(await this.req(`/v1/runs/${runId}/report.pdf?kind=${encodeURIComponent(kind)}`));\n }\n\n /** Poll until the run reaches a terminal state. Rejects on failure/timeout. */\n async wait(runId: string, opts: WaitOptions = {}): Promise<Run> {\n const intervalMs = opts.intervalMs ?? 5_000;\n const deadline = Date.now() + (opts.timeoutMs ?? 1_800_000);\n for (;;) {\n const run = await this.getRun(runId);\n if (run.status === \"failed\") throw new RunFailedError(run.runId, run.error ?? undefined);\n if (run.status === \"done\") return run;\n if (Date.now() >= deadline) throw new WaitTimeout(`run ${runId} not done in time`);\n await sleep(intervalMs);\n }\n }\n\n /** Upload, start a run, and (by default) wait for it to finish. */\n async analyze(file: FileInput, opts: AnalyzeOptions = {}): Promise<Run> {\n const videoId = await this.uploadVideo(file, { filename: opts.filename });\n const run = await this.createRun(videoId, opts);\n if (opts.wait === false) return run;\n return this.wait(run.runId, opts);\n }\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "creacortex",
3
+ "version": "0.2.0",
4
+ "description": "Node/TypeScript client for the Creacortex video-creative attention-analysis API",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md"
19
+ ],
20
+ "engines": {
21
+ "node": ">=18"
22
+ },
23
+ "sideEffects": false,
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "scripts": {
28
+ "build": "tsup",
29
+ "typecheck": "tsc --noEmit",
30
+ "test": "vitest run",
31
+ "prepublishOnly": "npm run build"
32
+ },
33
+ "keywords": [
34
+ "creacortex",
35
+ "video",
36
+ "advertising",
37
+ "attention",
38
+ "analytics",
39
+ "api",
40
+ "sdk"
41
+ ],
42
+ "license": "MIT",
43
+ "homepage": "https://creacortex.ai",
44
+ "devDependencies": {
45
+ "@types/node": "^20.0.0",
46
+ "tsup": "^8.0.0",
47
+ "typescript": "^5.4.0",
48
+ "vitest": "^2.0.0"
49
+ }
50
+ }