codai-sdk 0.67.0 → 0.69.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/dist/index.js CHANGED
@@ -1583,7 +1583,7 @@ function decodeBase64(b64) {
1583
1583
  for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
1584
1584
  return out;
1585
1585
  }
1586
- function abortableSleep$6(ms, signal) {
1586
+ function abortableSleep$7(ms, signal) {
1587
1587
  return new Promise((resolve, reject) => {
1588
1588
  if (signal?.aborted) return reject(signal.reason ?? /* @__PURE__ */ new Error("Aborted"));
1589
1589
  const t = setTimeout(() => {
@@ -1698,7 +1698,7 @@ var Videos = class {
1698
1698
  }
1699
1699
  if (st.status === "failed" || st.status === "filtered") throw new Error(`codai video job ${st.status}: ${st.error ?? "no details"}`);
1700
1700
  if (Date.now() + pollIntervalMs > deadline) throw new Error(`codai video job ${job.id} did not finish within ${timeoutMs} ms`);
1701
- await abortableSleep$6(pollIntervalMs, signal);
1701
+ await abortableSleep$7(pollIntervalMs, signal);
1702
1702
  }
1703
1703
  }
1704
1704
  };
@@ -1797,14 +1797,14 @@ var Renders = class {
1797
1797
  }
1798
1798
  if (st.status === "failed") throw new Error(`codai render failed: ${st.error ?? "no details"}`);
1799
1799
  if (Date.now() + pollIntervalMs > deadline) throw new Error(`codai render ${job.id} did not finish within ${timeoutMs} ms`);
1800
- await abortableSleep$6(pollIntervalMs, signal);
1800
+ await abortableSleep$7(pollIntervalMs, signal);
1801
1801
  }
1802
1802
  }
1803
1803
  };
1804
1804
 
1805
1805
  //#endregion
1806
1806
  //#region src/resources/documents.ts
1807
- function abortableSleep$5(ms, signal) {
1807
+ function abortableSleep$6(ms, signal) {
1808
1808
  return new Promise((resolve, reject) => {
1809
1809
  if (signal?.aborted) return reject(signal.reason ?? /* @__PURE__ */ new Error("Aborted"));
1810
1810
  const t = setTimeout(() => {
@@ -1897,7 +1897,7 @@ var Documents = class {
1897
1897
  if (st.status === "succeeded") return st;
1898
1898
  if (st.status === "failed") throw new Error(`codai document preview failed: ${st.error ?? "no details"}`);
1899
1899
  if (Date.now() + pollIntervalMs > deadline) throw new Error(`codai document preview ${job.id} did not finish within ${timeoutMs} ms`);
1900
- await abortableSleep$5(pollIntervalMs, signal);
1900
+ await abortableSleep$6(pollIntervalMs, signal);
1901
1901
  }
1902
1902
  }
1903
1903
  /** Queue a deck video and poll `renders.retrieve` until it succeeds. Throws on `failed` or timeout. */
@@ -1929,7 +1929,7 @@ var Documents = class {
1929
1929
  }
1930
1930
  if (st.status === "failed") throw new Error(`codai deck video failed: ${st.error ?? "no details"}`);
1931
1931
  if (Date.now() + pollIntervalMs > deadline) throw new Error(`codai deck video ${accepted.id} did not finish within ${timeoutMs} ms`);
1932
- await abortableSleep$5(pollIntervalMs, signal);
1932
+ await abortableSleep$6(pollIntervalMs, signal);
1933
1933
  }
1934
1934
  }
1935
1935
  };
@@ -1937,7 +1937,7 @@ var Documents = class {
1937
1937
  //#endregion
1938
1938
  //#region src/resources/exports.ts
1939
1939
  const path$6 = (id) => `/v1/studio/exports/${encodeURIComponent(id)}`;
1940
- function abortableSleep$4(ms, signal) {
1940
+ function abortableSleep$5(ms, signal) {
1941
1941
  return new Promise((resolve, reject) => {
1942
1942
  if (signal?.aborted) return reject(signal.reason ?? /* @__PURE__ */ new Error("Aborted"));
1943
1943
  const t = setTimeout(() => {
@@ -1978,13 +1978,15 @@ var Exports = class {
1978
1978
  });
1979
1979
  if (st.status === "succeeded" || st.status === "failed") return st;
1980
1980
  if (Date.now() + intervalMs > deadline) throw new Error(`codai export ${id} did not finish within ${timeoutMs} ms`);
1981
- await abortableSleep$4(intervalMs, signal);
1981
+ await abortableSleep$5(intervalMs, signal);
1982
1982
  }
1983
1983
  }
1984
1984
  };
1985
1985
 
1986
1986
  //#endregion
1987
1987
  //#region src/resources/creatives.ts
1988
+ /** True when a `build` answer is the 202 carousel / print export job (poll `exports.wait(r.export.id)`). */
1989
+ const isPagedBuild = (r) => "export" in r && r.export != null;
1988
1990
  const path$5 = (id, tail = "") => `/v1/studio/creatives/${encodeURIComponent(id)}${tail}`;
1989
1991
  const lottieQuery = (q) => ({
1990
1992
  format: q.format,
@@ -2159,6 +2161,24 @@ var Creatives = class {
2159
2161
  });
2160
2162
  }
2161
2163
  /**
2164
+ * Mixdown + mastering + exports of an `audio` creative (MU-05; same `POST …/render` route, HTTP 202). `formats`
2165
+ * (default `['wav', 'mp3']`), `stems: true` adds one WAV per audible track (+50 %), `lrc` exports the timed lyrics
2166
+ * (default on when the document has them), `loudness` is `streaming` (−14) | `podcast` (−16) | `broadcast` (−23) or
2167
+ * LUFS (−40…−5). Poll `renders.retrieve(render_id)`: the finished render's `data[]` holds one signed asset per output
2168
+ * (`output: mix | stem | lrc`). $0.001 per output second, minimum $0.01; held up front, billed on the measured length.
2169
+ * 400 `empty_audio` / `too_long` (20 min) / `no_lyrics`; video creatives use `render`.
2170
+ */
2171
+ renderAudio(id, body = {}, opts = {}) {
2172
+ return this.http.json(path$5(id, "/render"), {
2173
+ method: "POST",
2174
+ body,
2175
+ headers: opts.idempotencyKey ? { "Idempotency-Key": opts.idempotencyKey } : void 0,
2176
+ ext: opts.ext,
2177
+ signal: opts.signal,
2178
+ noRetry: !opts.idempotencyKey
2179
+ });
2180
+ }
2181
+ /**
2162
2182
  * PNG still frames at up to 6 times (`t_ms`). `exact: false` (default) screenshots the compiled HTML; `exact: true`
2163
2183
  * cuts them from a draft render of the covering scenes. Waits up to ~90 s: frames (`status: 'succeeded'`) or a
2164
2184
  * running job (`status: 'running'`) to poll with `frameJob(id, frame_job_id)`. Cached frames are free; 60 frame
@@ -2308,6 +2328,11 @@ var Creatives = class {
2308
2328
  * Studio document (`document`, exactly the `POST /v1/studio/documents` body). Converter notes come back in
2309
2329
  * `warnings`. The file is free; `preview: true` also queues a billed preview (poll it with
2310
2330
  * `documents.waitForPreview`). 400 `not_supported_for_kind` (video / image) or `invalid_spec`.
2331
+ *
2332
+ * A carousel (`format: 'png'` zip of slides or `'pdf'`) or print (`'pdf'`, PDF/X-4 with bleed and optional
2333
+ * `crop_marks`) creative answers HTTP 202 with a queued export job in `export` (`format_id` picks the format,
2334
+ * `quote_micro_usd` is held). Tell the two apart with `isPagedBuild`, poll with `exports.wait(r.export.id)` or use
2335
+ * `buildAndWait`.
2311
2336
  */
2312
2337
  build(id, body = {}, opts = {}) {
2313
2338
  return this.http.json(path$5(id, "/build"), {
@@ -2319,6 +2344,69 @@ var Creatives = class {
2319
2344
  });
2320
2345
  }
2321
2346
  /**
2347
+ * `build`, then — for a carousel / print creative — poll the export job until it has `succeeded` or `failed`
2348
+ * (`exports.wait`). Resolves to the 200 build for deck / doc / sheet, or to the finished `StudioExport`.
2349
+ */
2350
+ async buildAndWait(id, body = {}, opts = {}) {
2351
+ const r = await this.build(id, body, {
2352
+ ext: opts.ext,
2353
+ signal: opts.signal
2354
+ });
2355
+ if (!isPagedBuild(r)) return r;
2356
+ const e = r.export;
2357
+ if (e.status === "succeeded" || e.status === "failed") return e;
2358
+ return new Exports(this.http).wait(e.id, opts);
2359
+ }
2360
+ /**
2361
+ * Social post copy for a carousel, image or video creative: a caption + hashtags per platform (`platforms`,
2362
+ * default Instagram + LinkedIn; `language` `ro` (default) | `en`; optional `brief`, `tone`, `seq`) and one alt text
2363
+ * per slide, every answer repaired to the platform's hard limits (changes listed in `fixes`). Nothing is written to
2364
+ * the creative. Token-metered (`cost_micro_usd`) under a small wallet hold (402 `insufficient_funds`); 400
2365
+ * `not_supported_for_kind`, 422 `no_post_copy`, 504 `timeout`. `idempotencyKey` replays a retry (24 h).
2366
+ */
2367
+ postCopy(id, body = {}, opts = {}) {
2368
+ return this.http.json(path$5(id, "/post-copy"), {
2369
+ method: "POST",
2370
+ body: stripUndefined(body),
2371
+ headers: opts.idempotencyKey ? { "Idempotency-Key": opts.idempotencyKey } : void 0,
2372
+ ext: opts.ext,
2373
+ signal: opts.signal,
2374
+ noRetry: !opts.idempotencyKey
2375
+ });
2376
+ }
2377
+ /**
2378
+ * Edit a video clip by its transcript: delete word ranges (`remove: [{ from_word, to_word }]`, indexes into the
2379
+ * clip asset's cached transcript from `captions`) from video node `node_id`; the pieces are placed back to back as
2380
+ * one new version (`base_seq` rebased like `edit`, locks honoured unless `force`). Free. 409 `captions_required`
2381
+ * (run `captions` on that clip first) or `stale`; video creatives only.
2382
+ */
2383
+ transcriptCut(id, body, opts = {}) {
2384
+ return this.http.json(path$5(id, "/transcript-cut"), {
2385
+ method: "POST",
2386
+ body: stripUndefined(body),
2387
+ ext: opts.ext,
2388
+ signal: opts.signal,
2389
+ noRetry: true
2390
+ });
2391
+ }
2392
+ /**
2393
+ * Generate a new picture for an `image` node (`prompt`, optional `model`, `aspect_ratio` — default the closest
2394
+ * ratio of the node box — `base_seq`, `force`) and swap it in as one new version. A node with a `castId` sends that
2395
+ * cast entry's `refAssetIds` as references and appends its `description`, so the subject stays consistent. Billed
2396
+ * like `images.generate` (402 `insufficient_funds`); 400 `content_filtered` charges nothing. `idempotencyKey`
2397
+ * replays a retry (24 h).
2398
+ */
2399
+ regenerateNode(id, nodeId, body, opts = {}) {
2400
+ return this.http.json(path$5(id, `/nodes/${encodeURIComponent(nodeId)}/regenerate`), {
2401
+ method: "POST",
2402
+ body: stripUndefined(body),
2403
+ headers: opts.idempotencyKey ? { "Idempotency-Key": opts.idempotencyKey } : void 0,
2404
+ ext: opts.ext,
2405
+ signal: opts.signal,
2406
+ noRetry: !opts.idempotencyKey
2407
+ });
2408
+ }
2409
+ /**
2322
2410
  * Re-apply a brand kit (`brand_kit_id`) or a flat token map (`tokens`) to the creative as one new version: literal
2323
2411
  * colours equal to the old brand become token refs, `snap: true` maps every other colour to the nearest kit colour,
2324
2412
  * `fonts` (default true) sets heading / body font refs by role, locked nodes are skipped unless `force`. The kit
@@ -2705,7 +2793,7 @@ var Templates = class {
2705
2793
  //#endregion
2706
2794
  //#region src/resources/productions.ts
2707
2795
  const path$2 = (id) => `/v1/studio/productions/${encodeURIComponent(id)}`;
2708
- function abortableSleep$3(ms, signal) {
2796
+ function abortableSleep$4(ms, signal) {
2709
2797
  return new Promise((resolve, reject) => {
2710
2798
  if (signal?.aborted) return reject(signal.reason ?? /* @__PURE__ */ new Error("Aborted"));
2711
2799
  const t = setTimeout(() => {
@@ -2754,7 +2842,7 @@ var Productions = class {
2754
2842
  const p = await this.retrieve(id, opts);
2755
2843
  if (isProductionFinished(p)) return p;
2756
2844
  if (Date.now() + interval > deadline) throw new Error(`codai: production ${id} did not finish in time (phase ${p.phase})`);
2757
- await abortableSleep$3(interval, opts.signal);
2845
+ await abortableSleep$4(interval, opts.signal);
2758
2846
  }
2759
2847
  }
2760
2848
  /** `create` + `wait`. */
@@ -2764,6 +2852,293 @@ var Productions = class {
2764
2852
  }
2765
2853
  };
2766
2854
 
2855
+ //#endregion
2856
+ //#region src/resources/media-ops.ts
2857
+ const BASE$1 = "/v1/studio/media-ops";
2858
+ const opPath = (op) => `${BASE$1}/${encodeURIComponent(op)}`;
2859
+ function abortableSleep$3(ms, signal) {
2860
+ return new Promise((resolve, reject) => {
2861
+ if (signal?.aborted) return reject(signal.reason ?? /* @__PURE__ */ new Error("Aborted"));
2862
+ const t = setTimeout(() => {
2863
+ signal?.removeEventListener("abort", onAbort);
2864
+ resolve();
2865
+ }, ms);
2866
+ const onAbort = () => {
2867
+ clearTimeout(t);
2868
+ reject(signal?.reason ?? /* @__PURE__ */ new Error("Aborted"));
2869
+ };
2870
+ signal?.addEventListener("abort", onAbort, { once: true });
2871
+ });
2872
+ }
2873
+ const toBody = (input) => ({
2874
+ asset_ids: input.assetIds ?? [],
2875
+ params: input.params ?? {},
2876
+ ...input.maskPngB64 !== void 0 ? { mask_png_b64: input.maskPngB64 } : {}
2877
+ });
2878
+ /** A run answer is a job (async op, HTTP 202) when `object` is `media_op.job`. */
2879
+ const isMediaOpJob = (r) => r.object === "media_op.job";
2880
+ const isMediaOpJobFinished = (j) => j.status === "ready" || j.status === "failed";
2881
+ var MediaOps = class {
2882
+ http;
2883
+ constructor(http) {
2884
+ this.http = http;
2885
+ }
2886
+ /** `GET /v1/studio/media-ops` — every op with engine, mode, inputs, `available` and a list quote (free). */
2887
+ list(opts = {}) {
2888
+ return this.http.json(BASE$1, {
2889
+ ext: opts.ext,
2890
+ signal: opts.signal
2891
+ });
2892
+ }
2893
+ /** `POST /v1/studio/media-ops/{op}/quote` — exact price of this request; nothing is held or billed. */
2894
+ quote(op, input = {}, opts = {}) {
2895
+ return this.http.json(`${opPath(op)}/quote`, {
2896
+ method: "POST",
2897
+ body: toBody(input),
2898
+ ext: opts.ext,
2899
+ signal: opts.signal
2900
+ });
2901
+ }
2902
+ /**
2903
+ * `POST /v1/studio/media-ops/{op}` — sync ops resolve to a `media_op.result` with the outputs, async ops to a
2904
+ * `media_op.job` (`status: pending`; poll with `job` / `wait`). Use `isMediaOpJob` to tell them apart.
2905
+ */
2906
+ run(op, input = {}, opts = {}) {
2907
+ return this.http.json(opPath(op), {
2908
+ method: "POST",
2909
+ body: toBody(input),
2910
+ headers: opts.idempotencyKey ? { "Idempotency-Key": opts.idempotencyKey } : void 0,
2911
+ noRetry: !opts.idempotencyKey,
2912
+ ext: opts.ext,
2913
+ signal: opts.signal
2914
+ });
2915
+ }
2916
+ /** `GET /v1/studio/media-ops/jobs/{id}` — the job; reading a finished one stores its outputs and bills it once. */
2917
+ job(id, opts = {}) {
2918
+ return this.http.json(`${BASE$1}/jobs/${encodeURIComponent(id)}`, {
2919
+ ext: opts.ext,
2920
+ signal: opts.signal
2921
+ });
2922
+ }
2923
+ /**
2924
+ * Poll `job` every `intervalMs` (default 3 s) until it is `ready` or `failed` (a failed job resolves too — check
2925
+ * `status` / `error`). Throws after `timeoutMs` (default 20 min) or when `signal` aborts.
2926
+ */
2927
+ async wait(id, opts = {}) {
2928
+ const interval = opts.intervalMs ?? 3e3;
2929
+ const deadline = Date.now() + (opts.timeoutMs ?? 12e5);
2930
+ for (;;) {
2931
+ const j = await this.job(id, opts);
2932
+ if (isMediaOpJobFinished(j)) return j;
2933
+ if (Date.now() + interval > deadline) throw new Error(`codai: media job ${id} did not finish in time (status ${j.status})`);
2934
+ await abortableSleep$3(interval, opts.signal);
2935
+ }
2936
+ }
2937
+ /** `run`, then `wait` when the op is async. */
2938
+ async runAndWait(op, input = {}, opts = {}) {
2939
+ const r = await this.run(op, input, opts);
2940
+ return isMediaOpJob(r) && !isMediaOpJobFinished(r) ? this.wait(r.id, opts) : r;
2941
+ }
2942
+ };
2943
+
2944
+ //#endregion
2945
+ //#region src/resources/lyrics.ts
2946
+ const BASE = "/v1/studio/lyrics";
2947
+ var LyricsResource = class {
2948
+ http;
2949
+ constructor(http) {
2950
+ this.http = http;
2951
+ }
2952
+ post(path, body, opts) {
2953
+ return this.http.json(path, {
2954
+ method: "POST",
2955
+ body,
2956
+ headers: opts.idempotencyKey ? { "Idempotency-Key": opts.idempotencyKey } : void 0,
2957
+ noRetry: !opts.idempotencyKey,
2958
+ ext: opts.ext,
2959
+ signal: opts.signal
2960
+ });
2961
+ }
2962
+ /**
2963
+ * `POST /v1/studio/lyrics` — `{ brief, language: 'ro' | 'en', genre?, mood?, structure?, syllables_per_line?,
2964
+ * rhyme_scheme?, lines_per_section?, title? }` → `studio.lyrics` with every line's syllables and rhyme letter, the
2965
+ * whole song as `text` (`[kind]` headers) and a `report`. `repaired: true` when the repair turn won.
2966
+ */
2967
+ write(body, opts = {}) {
2968
+ return this.post(BASE, body, opts);
2969
+ }
2970
+ /**
2971
+ * `POST /v1/studio/lyrics/align` — `{ creative_id, clip_id, lyrics_text, language?, apply?, base_seq?,
2972
+ * retranscribe? }` → `studio.lyrics_alignment`: timed `lines`, `lrc`, `match_ratio`, `cached`, and `seq` /
2973
+ * `doc_hash` of the new version when applied.
2974
+ */
2975
+ align(body, opts = {}) {
2976
+ return this.post(`${BASE}/align`, body, opts);
2977
+ }
2978
+ };
2979
+
2980
+ //#endregion
2981
+ //#region src/resources/jam.ts
2982
+ const JAM_SUBPROTOCOL = "codai-jam";
2983
+ /** WebSocket close codes of `/v1/studio/jam`. */
2984
+ const JAM_CLOSE_CODES = {
2985
+ invalidMessage: 4400,
2986
+ unauthorized: 4401,
2987
+ insufficientFunds: 4402,
2988
+ forbidden: 4403,
2989
+ maxDuration: 4408,
2990
+ busy: 4409,
2991
+ upstream: 4502,
2992
+ unavailable: 4503
2993
+ };
2994
+ const JAM_SCALES = [
2995
+ "SCALE_UNSPECIFIED",
2996
+ "C_MAJOR_A_MINOR",
2997
+ "D_FLAT_MAJOR_B_FLAT_MINOR",
2998
+ "D_MAJOR_B_MINOR",
2999
+ "E_FLAT_MAJOR_C_MINOR",
3000
+ "E_MAJOR_D_FLAT_MINOR",
3001
+ "F_MAJOR_D_MINOR",
3002
+ "G_FLAT_MAJOR_E_FLAT_MINOR",
3003
+ "G_MAJOR_E_MINOR",
3004
+ "A_FLAT_MAJOR_F_MINOR",
3005
+ "A_MAJOR_G_FLAT_MINOR",
3006
+ "B_FLAT_MAJOR_G_MINOR",
3007
+ "B_MAJOR_A_FLAT_MINOR"
3008
+ ];
3009
+ const OPEN$1 = 1;
3010
+ function toArrayBuffer(data) {
3011
+ if (data instanceof ArrayBuffer) return data;
3012
+ if (ArrayBuffer.isView(data)) {
3013
+ const v = data;
3014
+ return v.buffer.slice(v.byteOffset, v.byteOffset + v.byteLength);
3015
+ }
3016
+ return null;
3017
+ }
3018
+ /** A live jam session: `on()` listeners for status / audio / errors and typed senders. */
3019
+ var JamSession = class {
3020
+ socket;
3021
+ listeners = /* @__PURE__ */ new Map();
3022
+ outbox = [];
3023
+ closed = false;
3024
+ /** Resolves when the socket is open (rejects if it closes first). */
3025
+ ready;
3026
+ constructor(socket) {
3027
+ this.socket = socket;
3028
+ let resolveReady;
3029
+ let rejectReady;
3030
+ this.ready = new Promise((res, rej) => {
3031
+ resolveReady = res;
3032
+ rejectReady = rej;
3033
+ });
3034
+ this.ready.catch(() => void 0);
3035
+ socket.onopen = () => {
3036
+ for (const f of this.outbox.splice(0)) socket.send(f);
3037
+ resolveReady();
3038
+ };
3039
+ socket.onmessage = (ev) => {
3040
+ if (typeof ev.data !== "string") {
3041
+ const buf = toArrayBuffer(ev.data);
3042
+ if (buf) this.emit({
3043
+ type: "audio",
3044
+ data: buf
3045
+ });
3046
+ return;
3047
+ }
3048
+ let parsed;
3049
+ try {
3050
+ parsed = JSON.parse(ev.data);
3051
+ } catch {
3052
+ return;
3053
+ }
3054
+ if (parsed && typeof parsed === "object" && typeof parsed.type === "string") this.emit(parsed);
3055
+ };
3056
+ socket.onerror = () => {};
3057
+ socket.onclose = (ev) => {
3058
+ rejectReady(/* @__PURE__ */ new Error(`jam socket closed before open (${String(ev.code)} ${ev.reason})`));
3059
+ this.closed = true;
3060
+ this.emit({
3061
+ type: "close",
3062
+ code: ev.code,
3063
+ reason: ev.reason
3064
+ });
3065
+ };
3066
+ }
3067
+ on(type, fn) {
3068
+ let set = this.listeners.get(type);
3069
+ if (!set) this.listeners.set(type, set = /* @__PURE__ */ new Set());
3070
+ set.add(fn);
3071
+ return () => set.delete(fn);
3072
+ }
3073
+ /** Send one client message (JSON). Queued until the socket opens. */
3074
+ send(msg) {
3075
+ if (this.closed) throw new Error("jam session is closed");
3076
+ const frame = JSON.stringify(msg);
3077
+ if (this.socket.readyState === OPEN$1) this.socket.send(frame);
3078
+ else this.outbox.push(frame);
3079
+ }
3080
+ prompts(prompts) {
3081
+ this.send({
3082
+ type: "prompts",
3083
+ prompts
3084
+ });
3085
+ }
3086
+ config(config) {
3087
+ this.send({
3088
+ type: "config",
3089
+ ...config
3090
+ });
3091
+ }
3092
+ play() {
3093
+ this.send({ type: "play" });
3094
+ }
3095
+ pause() {
3096
+ this.send({ type: "pause" });
3097
+ }
3098
+ stop() {
3099
+ this.send({ type: "stop" });
3100
+ }
3101
+ reset() {
3102
+ this.send({ type: "reset" });
3103
+ }
3104
+ close(code = 1e3, reason = "client closed") {
3105
+ if (this.closed) return;
3106
+ this.socket.close(code, reason);
3107
+ }
3108
+ emit(ev) {
3109
+ for (const fn of this.listeners.get(ev.type) ?? []) fn(ev);
3110
+ for (const fn of this.listeners.get("*") ?? []) fn(ev);
3111
+ }
3112
+ };
3113
+ /** Build the `wss://…/v1/studio/jam` URL (exported for tests and custom transports). */
3114
+ function jamUrl(baseUrl, opts = {}) {
3115
+ const url = new URL(`${baseUrl.replace(/\/$/, "").replace(/^http/, "ws")}/v1/studio/jam`);
3116
+ if (opts.sessionId) url.searchParams.set("session_id", opts.sessionId);
3117
+ return url.toString();
3118
+ }
3119
+ var Jam = class {
3120
+ defaults;
3121
+ constructor(defaults) {
3122
+ this.defaults = defaults;
3123
+ }
3124
+ /** Open a live jam. Returns immediately; `await session.ready` to wait for the socket. */
3125
+ connect(opts = {}) {
3126
+ const Ctor = opts.WebSocket ?? globalThis.WebSocket;
3127
+ if (!Ctor) throw new Error("codai.jam: no WebSocket available — pass options.WebSocket");
3128
+ let token = opts.token;
3129
+ let apiKey = token ? void 0 : opts.apiKey ?? this.defaults.apiKey;
3130
+ if (apiKey?.startsWith("codai_eph_v1.")) {
3131
+ token = apiKey;
3132
+ apiKey = void 0;
3133
+ }
3134
+ if (!token && !apiKey) throw new Error("codai.jam: pass `token` (browser) or `apiKey`");
3135
+ const url = jamUrl(opts.baseUrl ?? this.defaults.baseUrl, opts);
3136
+ const socket = apiKey ? new Ctor(url, [JAM_SUBPROTOCOL], { headers: { Authorization: `Bearer ${apiKey}` } }) : new Ctor(url, [JAM_SUBPROTOCOL, `bearer.${token}`]);
3137
+ if ("binaryType" in socket) socket.binaryType = "arraybuffer";
3138
+ return new JamSession(socket);
3139
+ }
3140
+ };
3141
+
2767
3142
  //#endregion
2768
3143
  //#region src/resources/batches.ts
2769
3144
  const BATCH_DONE = [
@@ -6453,6 +6828,12 @@ const OPERATION_METHODS = {
6453
6828
  listCreativeTemplates: "templates.list",
6454
6829
  createStudioProduction: "productions.create",
6455
6830
  getStudioProduction: "productions.retrieve",
6831
+ listStudioMediaOps: "mediaOps.list",
6832
+ quoteStudioMediaOp: "mediaOps.quote",
6833
+ runStudioMediaOp: "mediaOps.run",
6834
+ getStudioMediaOpJob: "mediaOps.job",
6835
+ writeStudioLyrics: "lyrics.write",
6836
+ alignStudioLyrics: "lyrics.align",
6456
6837
  getCreativeTemplate: "templates.retrieve",
6457
6838
  deleteCreativeTemplate: "templates.delete",
6458
6839
  exportCreativeFcpxml: "creatives.exportFcpxml",
@@ -6460,6 +6841,9 @@ const OPERATION_METHODS = {
6460
6841
  getStudioExport: "exports.retrieve",
6461
6842
  createCreativeFromDocument: "creatives.fromDocument",
6462
6843
  buildCreative: "creatives.build",
6844
+ writeCreativePostCopy: "creatives.postCopy",
6845
+ transcriptCutCreative: "creatives.transcriptCut",
6846
+ regenerateCreativeNode: "creatives.regenerateNode",
6463
6847
  createSystemOne: "systemOne.create",
6464
6848
  createTranscription: "audio.transcribe",
6465
6849
  createSpeech: "audio.speech",
@@ -6921,6 +7305,13 @@ var CodaiApps = class {
6921
7305
  * built-in starters; free; use with creatives.create({ kind, template, vars }))
6922
7306
  * client.productions.create / .retrieve / .wait / .run (codai Studio brief → promo video: storyboard, images,
6923
7307
  * voice-over, editable creative, draft render, one review + repair; billed as the parts are produced)
7308
+ * client.mediaOps.list / .quote / .run / .job / .wait / .runAndWait (codai Studio media operations on your assets:
7309
+ * background removal, upscale, inpaint / outpaint / erase, stems, denoise, MIDI, mastering, analysis, ACE-Step
7310
+ * music and sound effects; quote held, measured cost billed; async ops return a job)
7311
+ * client.lyrics.write / .align (codai Studio lyrics writer with syllable + rhyme checks; karaoke alignment to a clip
7312
+ * of an audio creative with LRC; token-metered / transcription billed)
7313
+ * client.creatives.renderAudio (mixdown + mastering of an audio creative: WAV / MP3 / FLAC, stems, LRC)
7314
+ * client.jam.connect (WebSocket live jam on Lyria RealTime; not an OpenAPI operation)
6924
7315
  * client.exports.retrieve / .wait (codai Studio FCPXML exports queued with creatives.exportFcpxml; only newly
6925
7316
  * rendered seconds are billed; Lottie exports from creatives.exportLottie are free and ready at once)
6926
7317
  * client.batches.create / .dryRun / .list / .get / .cancel / .retry / .exportCsv / .zip / .wait (codai Studio
@@ -6989,6 +7380,12 @@ var Codai = class {
6989
7380
  templates;
6990
7381
  /** codai Studio productions (`productions.run({ brief, language: 'ro' })` → a finished promo video + editable creative). */
6991
7382
  productions;
7383
+ /** codai Studio media operations (`mediaOps.run('audio.separate_stems', { assetIds })` → job; `mediaOps.wait(id)`). */
7384
+ mediaOps;
7385
+ /** codai Studio lyrics (`lyrics.write({ brief, language: 'ro' })`, `lyrics.align({ creative_id, clip_id, lyrics_text })`). */
7386
+ lyrics;
7387
+ /** codai Studio live jam over WebSocket (`jam.connect({ token })` → PCM16 48 kHz stereo; `prompts`, `config`, `play`). */
7388
+ jam;
6992
7389
  /** codai Studio FCPXML exports (`creatives.exportFcpxml(id)` queues one; `exports.wait(id)` polls until the zip is ready). */
6993
7390
  exports;
6994
7391
  /** codai Studio batches (`batches.create({ source: { template_id }, csv })` → one creative per row; `batches.wait(id)`). */
@@ -7093,6 +7490,12 @@ var Codai = class {
7093
7490
  this.brandKits = new BrandKits(http);
7094
7491
  this.templates = new Templates(http);
7095
7492
  this.productions = new Productions(http);
7493
+ this.mediaOps = new MediaOps(http);
7494
+ this.lyrics = new LyricsResource(http);
7495
+ this.jam = new Jam({
7496
+ baseUrl: http.baseUrl,
7497
+ apiKey: opts.apiKey
7498
+ });
7096
7499
  this.exports = new Exports(http);
7097
7500
  this.batches = new Batches(http);
7098
7501
  this.shares = new Shares(http);
@@ -7227,5 +7630,5 @@ function allKeys(obj) {
7227
7630
  }
7228
7631
 
7229
7632
  //#endregion
7230
- export { AbuseReports, Account, AgentRuns, Agents, Assets, Audio, Batches, Billing, BrandKits, Cards, Chat, ChatCompletions, Codai, Codai as default, CodaiApps, CodaiError, Creatives, Devices, Documents, Embeddings, EnvironmentFiles, EnvironmentJobs, EnvironmentMembers, EnvironmentPorts, EnvironmentSecrets, EnvironmentTasks, Environments, Exports, Feedback, Health, Heartbeats, Hosts, HttpClient, Images, Keys, Lists, Memory, Messages, Models, Music, Notifications, OPERATION_METHODS, OrgMembers, Orgs, OsGate, PROVENANCE_MAX_BYTES, PhoneModels, Productions, Projects, Prompts, Provenance, RESOLVE_ATTESTATION_PAYLOAD_TYPE, RESOLVE_OPERATION_METHODS, RESOLVE_TERMINAL_STATUSES, Realtime, RealtimeSession, Receipts, Referrals, Renders, Research, Resolve, ResolveError, ResolveInsufficientFundsError, Responses, STREAMING_OPERATIONS, SandboxFiles, Sandboxes, ScheduleExecutor, SchedulerHosts, Schedules, SessionControls, SessionEvents, SessionLease, SessionShares, Sessions, Settings, Sfx, Shares, SystemOne, TUS_CHUNK_BYTES, Tasks, Templates, Tokens, Tools, Transcripts, Triggers, Usage, Vault, VaultDevices, Verify, Videos, VoiceRevocations, extensionHeaders, frameJson, isAssetDto, isProductionFinished, isResearchFinished, mergeToolCall, readSse, realtimeUrl, resolveAttestationStatement, settingsConflictCurrent, shareTokenFrom, uploadIdFromLocation, vaultUnlockMessage, verifyAttestationWithKeys, verifyResolveWebhook };
7633
+ export { AbuseReports, Account, AgentRuns, Agents, Assets, Audio, Batches, Billing, BrandKits, Cards, Chat, ChatCompletions, Codai, Codai as default, CodaiApps, CodaiError, Creatives, Devices, Documents, Embeddings, EnvironmentFiles, EnvironmentJobs, EnvironmentMembers, EnvironmentPorts, EnvironmentSecrets, EnvironmentTasks, Environments, Exports, Feedback, Health, Heartbeats, Hosts, HttpClient, Images, JAM_CLOSE_CODES, JAM_SCALES, JAM_SUBPROTOCOL, Jam, JamSession, Keys, Lists, LyricsResource, MediaOps, Memory, Messages, Models, Music, Notifications, OPERATION_METHODS, OrgMembers, Orgs, OsGate, PROVENANCE_MAX_BYTES, PhoneModels, Productions, Projects, Prompts, Provenance, RESOLVE_ATTESTATION_PAYLOAD_TYPE, RESOLVE_OPERATION_METHODS, RESOLVE_TERMINAL_STATUSES, Realtime, RealtimeSession, Receipts, Referrals, Renders, Research, Resolve, ResolveError, ResolveInsufficientFundsError, Responses, STREAMING_OPERATIONS, SandboxFiles, Sandboxes, ScheduleExecutor, SchedulerHosts, Schedules, SessionControls, SessionEvents, SessionLease, SessionShares, Sessions, Settings, Sfx, Shares, SystemOne, TUS_CHUNK_BYTES, Tasks, Templates, Tokens, Tools, Transcripts, Triggers, Usage, Vault, VaultDevices, Verify, Videos, VoiceRevocations, extensionHeaders, frameJson, isAssetDto, isMediaOpJob, isMediaOpJobFinished, isPagedBuild, isProductionFinished, isResearchFinished, jamUrl, mergeToolCall, readSse, realtimeUrl, resolveAttestationStatement, settingsConflictCurrent, shareTokenFrom, uploadIdFromLocation, vaultUnlockMessage, verifyAttestationWithKeys, verifyResolveWebhook };
7231
7634
  //# sourceMappingURL=index.js.map