@nodaro/sdk 1.29.0 → 2.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/dist/index.cjs CHANGED
@@ -1,7 +1,41 @@
1
1
  'use strict';
2
2
 
3
- var prompts = require('@nodaro/prompts');
4
3
  var shared = require('@nodaro/shared');
4
+ var prompts = require('@nodaro/prompts');
5
+
6
+ // src/binary-response.ts
7
+ async function readBinaryResponse(response, maxBytes) {
8
+ const advertised = response.headers.get("content-length");
9
+ if (advertised !== null && (!/^\d+$/.test(advertised) || Number(advertised) > maxBytes)) {
10
+ await response.body?.cancel();
11
+ throw new Error("Binary response exceeds its declared size limit");
12
+ }
13
+ if (!response.body) return new ArrayBuffer(0);
14
+ const reader = response.body.getReader();
15
+ const chunks = [];
16
+ let length = 0;
17
+ try {
18
+ while (true) {
19
+ const next = await reader.read();
20
+ if (next.done) break;
21
+ length += next.value.byteLength;
22
+ if (length > maxBytes) {
23
+ await reader.cancel();
24
+ throw new Error("Binary response exceeds its declared size limit");
25
+ }
26
+ chunks.push(next.value);
27
+ }
28
+ } finally {
29
+ reader.releaseLock();
30
+ }
31
+ const bytes = new Uint8Array(length);
32
+ let offset = 0;
33
+ for (const chunk of chunks) {
34
+ bytes.set(chunk, offset);
35
+ offset += chunk.byteLength;
36
+ }
37
+ return bytes.buffer;
38
+ }
5
39
 
6
40
  // src/errors.ts
7
41
  var NodaroError = class extends Error {
@@ -59,8 +93,8 @@ var StorageExceededError = class extends NodaroError {
59
93
  limitBytes;
60
94
  };
61
95
  var WorkflowConflictError = class extends NodaroError {
62
- constructor(message = "Workflow was updated by another writer", currentUpdatedAt, currentVersion, currentRecord) {
63
- super(message, "workflow_conflict", 409);
96
+ constructor(message = "Workflow was updated by another writer", currentUpdatedAt, currentVersion, currentRecord, code = "workflow_conflict") {
97
+ super(message, code, 409);
64
98
  this.currentUpdatedAt = currentUpdatedAt;
65
99
  this.currentVersion = currentVersion;
66
100
  this.currentRecord = currentRecord;
@@ -70,6 +104,14 @@ var WorkflowConflictError = class extends NodaroError {
70
104
  currentVersion;
71
105
  currentRecord;
72
106
  };
107
+ var StudioOpError = class extends NodaroError {
108
+ constructor(message, code, status, opIndex) {
109
+ super(message, code, status);
110
+ this.opIndex = opIndex;
111
+ this.name = "StudioOpError";
112
+ }
113
+ opIndex;
114
+ };
73
115
  var JobBlockedError = class extends NodaroError {
74
116
  constructor(message = "Blocked by this deployment's content policy") {
75
117
  super(message, "job_blocked", 422);
@@ -116,14 +158,18 @@ function throwFromResponse(status, body) {
116
158
  const code = body.error?.code ?? "internal_error";
117
159
  const message = body.error?.message ?? "Request failed";
118
160
  if (status === 401) throw new UnauthorizedError(message);
119
- if (status === 409 && code === "workflow_conflict") {
161
+ if (status === 409 && (code === "workflow_conflict" || code === "production_busy")) {
120
162
  throw new WorkflowConflictError(
121
163
  message,
122
164
  body.error?.currentUpdatedAt,
123
165
  body.error?.currentVersion,
124
- body.error?.currentRecord
166
+ body.error?.currentRecord,
167
+ code
125
168
  );
126
169
  }
170
+ if (status >= 400 && status < 500 && typeof body.error?.opIndex === "number") {
171
+ throw new StudioOpError(message, code, status, body.error.opIndex);
172
+ }
127
173
  if (status === 422 && code === "job_blocked") throw new JobBlockedError(message);
128
174
  if (status === 403 && code === "insufficient_scope") {
129
175
  throw new ForbiddenError(message, body.error?.missingScope);
@@ -493,7 +539,8 @@ var NodesResource = class {
493
539
  return this.client.request("GET", `/v1/nodes/${encodeURIComponent(type)}`);
494
540
  }
495
541
  run(type, params = {}) {
496
- return this.client.request("POST", `/v1/${encodeURIComponent(type)}`, { body: params });
542
+ const route = type === "generate-3d-scene" ? "/v1/3d-scene/generate" : type === "edit-3d-scene" ? "/v1/3d-scene/edit" : type === "render-video" && typeof params.planType === "string" ? "/v1/render-video/plan" : `/v1/${encodeURIComponent(type)}`;
543
+ return this.client.request("POST", route, { body: params });
497
544
  }
498
545
  async runAndWait(type, params = {}, opts = {}) {
499
546
  if (opts.signal?.aborted) throw new JobAbortedError();
@@ -558,6 +605,58 @@ var NodesResource = class {
558
605
  }
559
606
  }
560
607
  };
608
+ var Scene3DResource = class {
609
+ constructor(client) {
610
+ this.client = client;
611
+ }
612
+ client;
613
+ capabilities() {
614
+ return this.client.request("GET", "/v1/3d-scene/capabilities");
615
+ }
616
+ /** Persist deterministic overlays without an LLM or a generation charge. */
617
+ applyEdits(revisionId, params) {
618
+ return this.client.request("POST", `/v1/3d-scene/revisions/${encodeURIComponent(revisionId)}/edits`, { body: params });
619
+ }
620
+ /** Asset access is scoped to an exact retained revision, with fresh authentication. */
621
+ assetBytes(revisionId, asset, options) {
622
+ if (!["glb", "camera-track-json", "poster", "validation-report"].includes(asset.kind)) {
623
+ throw new Error("This asset is not available through the playback endpoint");
624
+ }
625
+ if (!Number.isSafeInteger(asset.byteLength) || asset.byteLength < 1 || asset.byteLength > shared.SCENE3D_V2_LIMITS.maxRendererAssetBytes) {
626
+ throw new Error("Invalid scene asset byte length");
627
+ }
628
+ return this.client.requestBytes("GET", `/v1/3d-scene/revisions/${encodeURIComponent(revisionId)}/assets/${encodeURIComponent(asset.assetId)}`, {
629
+ signal: options?.signal,
630
+ maxBytes: asset.byteLength
631
+ });
632
+ }
633
+ /** The editable native file has its own authorization lane. */
634
+ sourceBytes(revisionId, options) {
635
+ return this.client.requestBytes("GET", `/v1/3d-scene/revisions/${encodeURIComponent(revisionId)}/source`, {
636
+ signal: options?.signal,
637
+ maxBytes: shared.SCENE3D_V2_LIMITS.maxBlendSourceBytes
638
+ });
639
+ }
640
+ generate(params) {
641
+ return this.client.nodes.run("generate-3d-scene", params);
642
+ }
643
+ generateAndWait(params, options) {
644
+ return this.client.nodes.runAndWait("generate-3d-scene", params, options);
645
+ }
646
+ edit(params) {
647
+ return this.client.nodes.run("edit-3d-scene", params);
648
+ }
649
+ editAndWait(params, options) {
650
+ return this.client.nodes.runAndWait("edit-3d-scene", params, options);
651
+ }
652
+ /** Uses the supplied immutable revision; never starts authoring or a rebuild. */
653
+ render(params) {
654
+ return this.client.nodes.run("render-video", params);
655
+ }
656
+ renderAndWait(params, options) {
657
+ return this.client.nodes.runAndWait("render-video", params, options);
658
+ }
659
+ };
561
660
 
562
661
  // src/resources/developer-apps.ts
563
662
  var DeveloperAppsResource = class {
@@ -2371,6 +2470,245 @@ var RecastResource = class {
2371
2470
  }
2372
2471
  };
2373
2472
 
2473
+ // src/resources/studio.ts
2474
+ function isStudioGenerateEstimate(result) {
2475
+ return result.dryRun === true;
2476
+ }
2477
+ var StudioProductionsResource = class {
2478
+ constructor(client) {
2479
+ this.client = client;
2480
+ }
2481
+ client;
2482
+ path(productionId, suffix = "") {
2483
+ return `/v1/studio/productions/${encodeURIComponent(productionId)}${suffix}`;
2484
+ }
2485
+ // ── reads ────────────────────────────────────────────────────────────────
2486
+ /**
2487
+ * The authoring guide, the full catalog, the plan's JSON Schema and the
2488
+ * operating guide — rendered server-side from the version that is live, so
2489
+ * they describe the platform you are actually talking to. Free.
2490
+ */
2491
+ async skill() {
2492
+ const res = await this.client.request(
2493
+ "GET",
2494
+ "/v1/studio/productions/skill"
2495
+ );
2496
+ return res.data;
2497
+ }
2498
+ /**
2499
+ * Check a plan before it becomes anything. Free, persists nothing, and
2500
+ * resolves cast names against YOUR library — loop on `errors` until
2501
+ * `valid: true`, then `create({ plan })`.
2502
+ */
2503
+ async validatePlan(plan) {
2504
+ const res = await this.client.request(
2505
+ "POST",
2506
+ "/v1/studio/productions/validate",
2507
+ { body: { plan } }
2508
+ );
2509
+ return res.data;
2510
+ }
2511
+ /** Your productions, newest first. Archived rows are hidden unless asked for. */
2512
+ async list(opts = {}) {
2513
+ const res = await this.client.request(
2514
+ "GET",
2515
+ "/v1/studio/productions",
2516
+ {
2517
+ query: {
2518
+ limit: opts.limit,
2519
+ cursor: opts.cursor,
2520
+ includeArchived: opts.includeArchived
2521
+ }
2522
+ }
2523
+ );
2524
+ return res.data;
2525
+ }
2526
+ /**
2527
+ * One production. A pure read — it never lands a finished job, so call
2528
+ * {@link reconcile} first if you are waiting on one.
2529
+ */
2530
+ async get(productionId, opts = {}) {
2531
+ const res = await this.client.request(
2532
+ "GET",
2533
+ this.path(productionId),
2534
+ { query: { detail: opts.detail, shot_id: opts.shotId } }
2535
+ );
2536
+ return res.data.production;
2537
+ }
2538
+ /**
2539
+ * The ordered steps that assemble the film, with what they will cost. Run
2540
+ * them yourself with the ordinary verbs — nothing here blocks for minutes,
2541
+ * and nothing here spends.
2542
+ */
2543
+ async exportPlan(productionId, opts = {}) {
2544
+ const res = await this.client.request(
2545
+ "GET",
2546
+ this.path(productionId, "/export-plan"),
2547
+ { query: { upscale: opts.upscale } }
2548
+ );
2549
+ return res.data;
2550
+ }
2551
+ // ── writes ───────────────────────────────────────────────────────────────
2552
+ /** A new production, optionally landed from a plan in the same call. */
2553
+ async create(input = {}) {
2554
+ const res = await this.client.request(
2555
+ "POST",
2556
+ "/v1/studio/productions",
2557
+ { body: input }
2558
+ );
2559
+ return res.data;
2560
+ }
2561
+ /**
2562
+ * Apply a batch of operations. Atomic: one bad operation refuses the whole
2563
+ * batch with a `StudioOpError` naming its index, and nothing is written. On
2564
+ * success adopt `production` wholesale and carry `version` forward as the
2565
+ * next `baseVersion`.
2566
+ */
2567
+ async ops(productionId, input) {
2568
+ const res = await this.client.request(
2569
+ "POST",
2570
+ this.path(productionId, "/ops"),
2571
+ { body: input }
2572
+ );
2573
+ return res.data;
2574
+ }
2575
+ /**
2576
+ * Land every generation that has finished since you last looked, and report
2577
+ * what is still running. The one call that turns finished jobs into results
2578
+ * without a browser open. It writes only when something actually landed.
2579
+ */
2580
+ async reconcile(productionId) {
2581
+ const res = await this.client.request(
2582
+ "POST",
2583
+ this.path(productionId, "/reconcile")
2584
+ );
2585
+ return res.data;
2586
+ }
2587
+ /** Add a plan's scenes to a production that already exists. */
2588
+ async importPlan(productionId, plan, opts = {}) {
2589
+ const res = await this.client.request(
2590
+ "POST",
2591
+ this.path(productionId, "/import"),
2592
+ { body: { plan, mode: opts.mode ?? "append" } }
2593
+ );
2594
+ return res.data;
2595
+ }
2596
+ /**
2597
+ * Turn a brief into scenes. The run is a platform job: poll it, and its
2598
+ * scenes land through {@link reconcile}. The production comes back with the
2599
+ * run recorded on it as a pending draft.
2600
+ */
2601
+ async describe(productionId, input) {
2602
+ const res = await this.client.request(
2603
+ "POST",
2604
+ this.path(productionId, "/describe"),
2605
+ { body: input }
2606
+ );
2607
+ return res.data;
2608
+ }
2609
+ async generate(productionId, input) {
2610
+ const res = await this.client.request(
2611
+ "POST",
2612
+ this.path(productionId, "/generate"),
2613
+ { body: input }
2614
+ );
2615
+ return res.data;
2616
+ }
2617
+ generateStill(productionId, shotId, opts = {}) {
2618
+ return this.generate(productionId, { ...opts, kind: "still", shotId });
2619
+ }
2620
+ generateClip(productionId, shotId, opts = {}) {
2621
+ return this.generate(productionId, { ...opts, kind: "clip", shotId });
2622
+ }
2623
+ // ── media on one shot ────────────────────────────────────────────────────
2624
+ /**
2625
+ * Extract a frame from a shot's active clip and put it where `target` says —
2626
+ * a new shot after this one (the default), this shot's sticky start or end
2627
+ * frame, or another still of the same shot. Seconds, not minutes: this one
2628
+ * waits for the job and answers with the changed production.
2629
+ */
2630
+ async frame(productionId, input) {
2631
+ const res = await this.client.request(
2632
+ "POST",
2633
+ this.path(productionId, "/frame"),
2634
+ { body: input }
2635
+ );
2636
+ return res.data;
2637
+ }
2638
+ /** Synthesize a shot's spoken line and record it. Waits for the job. */
2639
+ async voice(productionId, input) {
2640
+ const res = await this.client.request(
2641
+ "POST",
2642
+ this.path(productionId, "/voice"),
2643
+ { body: input }
2644
+ );
2645
+ return res.data;
2646
+ }
2647
+ /**
2648
+ * Recast the voices of a shot's active clip. Minutes of work, so it answers
2649
+ * `jobId` and the new clip lands as a result through its own marker.
2650
+ */
2651
+ async revoice(productionId, input) {
2652
+ const res = await this.client.request(
2653
+ "POST",
2654
+ this.path(productionId, "/revoice"),
2655
+ { body: input }
2656
+ );
2657
+ return res.data;
2658
+ }
2659
+ /** Score the film. The finished track lands through its pending marker. */
2660
+ async music(productionId, input) {
2661
+ const res = await this.client.request(
2662
+ "POST",
2663
+ this.path(productionId, "/music"),
2664
+ { body: input }
2665
+ );
2666
+ return res.data;
2667
+ }
2668
+ // ── audience and copies ──────────────────────────────────────────────────
2669
+ /**
2670
+ * Open the share-by-link read. Deliberately its own route rather than an
2671
+ * operation: who may see the work is decided by the owner, never as a side
2672
+ * effect of a batch that was editing something else.
2673
+ */
2674
+ async share(productionId) {
2675
+ const res = await this.client.request(
2676
+ "POST",
2677
+ this.path(productionId, "/share"),
2678
+ { body: { shared: true } }
2679
+ );
2680
+ return res.data.production;
2681
+ }
2682
+ /** Close it again. Sharing is opt-in AND reversible. */
2683
+ async unshare(productionId) {
2684
+ const res = await this.client.request(
2685
+ "POST",
2686
+ this.path(productionId, "/unshare")
2687
+ );
2688
+ return res.data.production;
2689
+ }
2690
+ /**
2691
+ * Copy a production you own or can see into your own Studio project. The
2692
+ * copy starts PRIVATE and visible — sharing and archiving never travel — and
2693
+ * it is copied through YOUR view of the source, so somebody else's bin does
2694
+ * not come with it.
2695
+ */
2696
+ async clone(productionId, input = {}) {
2697
+ const res = await this.client.request(
2698
+ "POST",
2699
+ this.path(productionId, "/clone"),
2700
+ { body: input }
2701
+ );
2702
+ return res.data.production;
2703
+ }
2704
+ };
2705
+ var StudioResource = class {
2706
+ productions;
2707
+ constructor(client) {
2708
+ this.productions = new StudioProductionsResource(client);
2709
+ }
2710
+ };
2711
+
2374
2712
  // src/resources/community.ts
2375
2713
  var CommunityResource = class {
2376
2714
  constructor(client) {
@@ -2752,7 +3090,7 @@ var WorkspacesResource = class {
2752
3090
  return this.client.requestText("GET", `/v1/workspaces/${encodeURIComponent(id)}/usage`, { query: { ...opts, format: "csv" } });
2753
3091
  }
2754
3092
  };
2755
- var SDK_VERSION = "1.29.0" ;
3093
+ var SDK_VERSION = "2.1.0" ;
2756
3094
  var CLIENT_HEADER = "X-Nodaro-Client";
2757
3095
  var isBrowser = () => typeof window !== "undefined" && typeof window.document !== "undefined";
2758
3096
  var NodaroClient = class _NodaroClient {
@@ -2779,6 +3117,7 @@ var NodaroClient = class _NodaroClient {
2779
3117
  videoPro;
2780
3118
  executions;
2781
3119
  nodes;
3120
+ scene3d;
2782
3121
  developerApps;
2783
3122
  oauth;
2784
3123
  apps;
@@ -2802,6 +3141,7 @@ var NodaroClient = class _NodaroClient {
2802
3141
  models;
2803
3142
  shots;
2804
3143
  recast;
3144
+ studio;
2805
3145
  community;
2806
3146
  templates;
2807
3147
  tutorials;
@@ -2823,6 +3163,7 @@ var NodaroClient = class _NodaroClient {
2823
3163
  this.videoPro = new VideoProResource(this);
2824
3164
  this.executions = new ExecutionsResource(this);
2825
3165
  this.nodes = new NodesResource(this);
3166
+ this.scene3d = new Scene3DResource(this);
2826
3167
  this.developerApps = new DeveloperAppsResource(this);
2827
3168
  this.oauth = new OAuthResource(this);
2828
3169
  this.apps = new AppsResource(this);
@@ -2846,6 +3187,7 @@ var NodaroClient = class _NodaroClient {
2846
3187
  this.models = new ModelsResource(this);
2847
3188
  this.shots = new ShotsResource(this);
2848
3189
  this.recast = new RecastResource(this);
3190
+ this.studio = new StudioResource(this);
2849
3191
  this.community = new CommunityResource(this);
2850
3192
  this.templates = new TemplatesResource(this);
2851
3193
  this.tutorials = new TutorialsResource(this);
@@ -2895,10 +3237,11 @@ var NodaroClient = class _NodaroClient {
2895
3237
  if (token) headers["Authorization"] = `Bearer ${token}`;
2896
3238
  const ac = new AbortController();
2897
3239
  const timeoutId = setTimeout(() => ac.abort(), this.timeoutMs);
2898
- if (options.signal) {
2899
- options.signal.addEventListener("abort", () => ac.abort(), { once: true });
2900
- }
3240
+ const abort = () => ac.abort(options.signal?.reason);
3241
+ options.signal?.addEventListener("abort", abort, { once: true });
3242
+ if (options.signal?.aborted) abort();
2901
3243
  try {
3244
+ ac.signal.throwIfAborted();
2902
3245
  const res = await this.fetch(url, {
2903
3246
  method,
2904
3247
  headers,
@@ -2916,8 +3259,14 @@ var NodaroClient = class _NodaroClient {
2916
3259
  return await read(res);
2917
3260
  } finally {
2918
3261
  clearTimeout(timeoutId);
3262
+ options.signal?.removeEventListener("abort", abort);
2919
3263
  }
2920
3264
  }
3265
+ /** Authenticated, bounded binary reads through the normal timeout/error transport. */
3266
+ async requestBytes(method, path, options) {
3267
+ if (!Number.isSafeInteger(options.maxBytes) || options.maxBytes < 1) throw new Error("maxBytes must be a positive integer");
3268
+ return this.send(method, path, options, (response) => readBinaryResponse(response, options.maxBytes));
3269
+ }
2921
3270
  async request(method, path, options = {}) {
2922
3271
  return this.send(
2923
3272
  method,
@@ -3000,22 +3349,6 @@ function supabaseAuth(supabase) {
3000
3349
  };
3001
3350
  }
3002
3351
 
3003
- Object.defineProperty(exports, "PEOPLE", {
3004
- enumerable: true,
3005
- get: function () { return prompts.PEOPLE; }
3006
- });
3007
- Object.defineProperty(exports, "PERSON_DIMENSION_LABELS", {
3008
- enumerable: true,
3009
- get: function () { return prompts.PERSON_DIMENSION_LABELS; }
3010
- });
3011
- Object.defineProperty(exports, "PERSON_DIMENSION_ORDER", {
3012
- enumerable: true,
3013
- get: function () { return prompts.PERSON_DIMENSION_ORDER; }
3014
- });
3015
- Object.defineProperty(exports, "buildPersonHints", {
3016
- enumerable: true,
3017
- get: function () { return prompts.buildPersonHints; }
3018
- });
3019
3352
  Object.defineProperty(exports, "CHARACTER_ASPECT_DEFAULTS", {
3020
3353
  enumerable: true,
3021
3354
  get: function () { return shared.CHARACTER_ASPECT_DEFAULTS; }
@@ -3084,6 +3417,22 @@ Object.defineProperty(exports, "WORKSPACE_HEADER", {
3084
3417
  enumerable: true,
3085
3418
  get: function () { return shared.WORKSPACE_HEADER; }
3086
3419
  });
3420
+ Object.defineProperty(exports, "PEOPLE", {
3421
+ enumerable: true,
3422
+ get: function () { return prompts.PEOPLE; }
3423
+ });
3424
+ Object.defineProperty(exports, "PERSON_DIMENSION_LABELS", {
3425
+ enumerable: true,
3426
+ get: function () { return prompts.PERSON_DIMENSION_LABELS; }
3427
+ });
3428
+ Object.defineProperty(exports, "PERSON_DIMENSION_ORDER", {
3429
+ enumerable: true,
3430
+ get: function () { return prompts.PERSON_DIMENSION_ORDER; }
3431
+ });
3432
+ Object.defineProperty(exports, "buildPersonHints", {
3433
+ enumerable: true,
3434
+ get: function () { return prompts.buildPersonHints; }
3435
+ });
3087
3436
  exports.AppsResource = AppsResource;
3088
3437
  exports.AudioResource = AudioResource;
3089
3438
  exports.CREATURE_ASSET_TYPES = CREATURE_ASSET_TYPES;
@@ -3123,9 +3472,13 @@ exports.PromptHelperResource = PromptHelperResource;
3123
3472
  exports.RateLimitedError = RateLimitedError;
3124
3473
  exports.RecastResource = RecastResource;
3125
3474
  exports.ReduceResource = ReduceResource;
3475
+ exports.Scene3DResource = Scene3DResource;
3126
3476
  exports.ShotsResource = ShotsResource;
3127
3477
  exports.StaticTokenAuth = StaticTokenAuth;
3128
3478
  exports.StorageExceededError = StorageExceededError;
3479
+ exports.StudioOpError = StudioOpError;
3480
+ exports.StudioProductionsResource = StudioProductionsResource;
3481
+ exports.StudioResource = StudioResource;
3129
3482
  exports.TemplatesResource = TemplatesResource;
3130
3483
  exports.TutorialsResource = TutorialsResource;
3131
3484
  exports.UnauthorizedError = UnauthorizedError;
@@ -3137,6 +3490,7 @@ exports.WorkflowsResource = WorkflowsResource;
3137
3490
  exports.WorkspacesResource = WorkspacesResource;
3138
3491
  exports.buildPersonSeedPrompt = buildPersonSeedPrompt;
3139
3492
  exports.createClient = createClient;
3493
+ exports.isStudioGenerateEstimate = isStudioGenerateEstimate;
3140
3494
  exports.supabaseAuth = supabaseAuth;
3141
3495
  exports.throwFromResponse = throwFromResponse;
3142
3496
  //# sourceMappingURL=index.cjs.map