@nodaro/sdk 2.1.0 → 2.4.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
@@ -538,13 +538,19 @@ var NodesResource = class {
538
538
  get(type) {
539
539
  return this.client.request("GET", `/v1/nodes/${encodeURIComponent(type)}`);
540
540
  }
541
- run(type, params = {}) {
541
+ run(type, params = {}, options = {}) {
542
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 });
543
+ return this.client.request("POST", route, {
544
+ body: params,
545
+ // Transport-level retry safety, opt-in per call. The platform reads the
546
+ // standard header, so a caller that reuses a key on a retry gets the
547
+ // same run back instead of a second charge.
548
+ ...options.idempotencyKey ? { headers: { "Idempotency-Key": options.idempotencyKey } } : {}
549
+ });
544
550
  }
545
551
  async runAndWait(type, params = {}, opts = {}) {
546
552
  if (opts.signal?.aborted) throw new JobAbortedError();
547
- const result = await this.run(type, params);
553
+ const result = await this.run(type, params, { idempotencyKey: opts.idempotencyKey });
548
554
  const jobId = extractJobId(result, type);
549
555
  return this.pollJob(jobId, type, opts);
550
556
  }
@@ -605,6 +611,10 @@ var NodesResource = class {
605
611
  }
606
612
  }
607
613
  };
614
+ function newIdempotencyKey() {
615
+ const random = globalThis.crypto?.randomUUID?.();
616
+ return `sdk-pro3d-${random ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`}`;
617
+ }
608
618
  var Scene3DResource = class {
609
619
  constructor(client) {
610
620
  this.client = client;
@@ -613,6 +623,22 @@ var Scene3DResource = class {
613
623
  capabilities() {
614
624
  return this.client.request("GET", "/v1/3d-scene/capabilities");
615
625
  }
626
+ /** Current access to both the delivery and its source is required on every read. */
627
+ getDelivery(jobId) {
628
+ return this.client.request("GET", `/v1/3d-scene/deliveries/${encodeURIComponent(jobId)}`);
629
+ }
630
+ deliveryAssetBytes(jobId, asset, options) {
631
+ if (asset.kind !== "poster" && asset.kind !== "validation-report" || asset.usage !== (asset.kind === "poster" ? "poster" : "validation")) {
632
+ throw new Error("This asset is not available through the delivery endpoint");
633
+ }
634
+ if (!Number.isSafeInteger(asset.byteLength) || asset.byteLength < 1 || asset.byteLength > shared.SCENE3D_V2_LIMITS.maxRendererAssetBytes) {
635
+ throw new Error("Invalid scene delivery asset byte length");
636
+ }
637
+ return this.client.requestBytes("GET", `/v1/3d-scene/deliveries/${encodeURIComponent(jobId)}/assets/${encodeURIComponent(asset.assetId)}`, {
638
+ signal: options?.signal,
639
+ maxBytes: asset.byteLength
640
+ });
641
+ }
616
642
  /** Persist deterministic overlays without an LLM or a generation charge. */
617
643
  applyEdits(revisionId, params) {
618
644
  return this.client.request("POST", `/v1/3d-scene/revisions/${encodeURIComponent(revisionId)}/edits`, { body: params });
@@ -649,6 +675,53 @@ var Scene3DResource = class {
649
675
  editAndWait(params, options) {
650
676
  return this.client.nodes.runAndWait("edit-3d-scene", params, options);
651
677
  }
678
+ /**
679
+ * Price a 3D Render Pro request WITHOUT starting it.
680
+ *
681
+ * Takes the same body a run takes, minus the `quoteId`, and answers a
682
+ * ceiling (`maxCredits`), a `breakdown` to show, and the
683
+ * `normalizedInputHash` admission re-checks. It reserves nothing and spends
684
+ * nothing — a quote is a price, not a purchase.
685
+ */
686
+ quotePro(params) {
687
+ return this.client.request("POST", "/v1/pro-3d-render/quote", { body: params });
688
+ }
689
+ /**
690
+ * Submit a quoted 3D Render Pro run.
691
+ *
692
+ * Requires the `quoteId` from {@link quotePro}: no run starts at a price
693
+ * nobody showed. Sends an `Idempotency-Key` — a fresh one per call unless you
694
+ * supply your own, which you should when retrying a call that timed out.
695
+ *
696
+ * Availability is a property of the deployment: `capabilities().pro` says
697
+ * whether this install can serve it, and which controls you may offer.
698
+ */
699
+ runPro(params, options) {
700
+ return this.client.nodes.run("pro-3d-render", params, {
701
+ idempotencyKey: options?.idempotencyKey ?? newIdempotencyKey()
702
+ });
703
+ }
704
+ /**
705
+ * Quote if needed, run, and wait — the whole operation in one call.
706
+ *
707
+ * Two HTTP requests at most, still ONE paid job: when `quoteId` is absent
708
+ * this quotes the identical body first, so the run is admitted against a
709
+ * hash of exactly what was priced. Pass a `quoteId` to run against a quote
710
+ * you already showed the user.
711
+ *
712
+ * Resolves with the settled result — `videoUrl` (the MP4) and `scenePlan`
713
+ * (the exact composition it was rendered from), plus the revision, poster,
714
+ * validation and renderer metadata. Re-render that same `scenePlan` later
715
+ * with {@link render}, or with a `{kind:'scene'}` source; that costs no
716
+ * authoring.
717
+ */
718
+ async renderProAndWait(params, options) {
719
+ const quoteId = typeof params.quoteId === "string" ? params.quoteId : (await this.quotePro(params)).quoteId;
720
+ return this.client.nodes.runAndWait("pro-3d-render", { ...params, quoteId }, {
721
+ ...options,
722
+ idempotencyKey: options?.idempotencyKey ?? newIdempotencyKey()
723
+ });
724
+ }
652
725
  /** Uses the supplied immutable revision; never starts authoring or a rebuild. */
653
726
  render(params) {
654
727
  return this.client.nodes.run("render-video", params);
@@ -2470,7 +2543,7 @@ var RecastResource = class {
2470
2543
  }
2471
2544
  };
2472
2545
 
2473
- // src/resources/studio.ts
2546
+ // src/resources/studio-productions.ts
2474
2547
  function isStudioGenerateEstimate(result) {
2475
2548
  return result.dryRun === true;
2476
2549
  }
@@ -2702,11 +2775,80 @@ var StudioProductionsResource = class {
2702
2775
  return res.data.production;
2703
2776
  }
2704
2777
  };
2778
+
2779
+ // src/resources/studio.ts
2780
+ var root = "/v1/studio/productions";
2781
+ var path = (id) => `${root}/${encodeURIComponent(id)}`;
2705
2782
  var StudioResource = class {
2706
- productions;
2707
2783
  constructor(client) {
2784
+ this.client = client;
2708
2785
  this.productions = new StudioProductionsResource(client);
2709
2786
  }
2787
+ client;
2788
+ productions;
2789
+ capabilities() {
2790
+ return this.client.request("GET", `${root}/capabilities`);
2791
+ }
2792
+ skill() {
2793
+ return this.client.request("GET", `${root}/skill`);
2794
+ }
2795
+ list(options = {}) {
2796
+ return this.client.request("GET", root, { query: options });
2797
+ }
2798
+ get(id, options = {}) {
2799
+ return this.client.request("GET", path(id), { query: { detail: options.detail, shot_id: options.shotId } });
2800
+ }
2801
+ validatePlan(plan) {
2802
+ return this.client.request("POST", `${root}/validate`, { body: { plan } });
2803
+ }
2804
+ create(input) {
2805
+ return this.client.request("POST", root, { body: input });
2806
+ }
2807
+ /** Import a portable document through the compatible server writer.
2808
+ * Retained frame media requires an authorized source; acceptance never transfers. */
2809
+ importBundle(input) {
2810
+ return this.client.request("POST", `${root}/import-bundle`, { body: input });
2811
+ }
2812
+ /** Append a bundle's scenes and frame closure under an exact destination revision. */
2813
+ appendBundle(id, input) {
2814
+ return this.client.request("POST", `${path(id)}/import-bundle`, { body: input });
2815
+ }
2816
+ /** Copy a saved production. Linked copies retain verified inputs, remap
2817
+ * dependencies and require fresh frame acceptance; they start no media jobs. */
2818
+ clone(id, input = {}) {
2819
+ return this.client.request("POST", `${path(id)}/clone`, { body: input });
2820
+ }
2821
+ edit(id, input) {
2822
+ return this.client.request("POST", `${path(id)}/ops`, { body: input });
2823
+ }
2824
+ /** Audience-authorized sharing; an expected revision prevents publication
2825
+ * of concurrent edits the caller has not reviewed. */
2826
+ setShared(id, input) {
2827
+ return this.client.request("POST", `${path(id)}/share`, { body: input });
2828
+ }
2829
+ /** Save ordinary editor fields against the loaded revision. Protected frame
2830
+ * and job state can only change through their dedicated semantic actions. */
2831
+ saveEditorState(id, input) {
2832
+ return this.edit(id, {
2833
+ baseVersion: input.expectedVersion,
2834
+ strict: true,
2835
+ ...input.clientRequestId ? { clientRequestId: input.clientRequestId } : {},
2836
+ ops: [{ op: "save_editor_state", expectedVersion: input.expectedVersion, graph: input.graph }]
2837
+ });
2838
+ }
2839
+ generateKeyframe(id, input) {
2840
+ return this.client.request("POST", `${path(id)}/generate`, { body: { ...input, kind: "keyframe" } });
2841
+ }
2842
+ generateShot(id, input) {
2843
+ return this.client.request("POST", `${path(id)}/generate`, { body: input });
2844
+ }
2845
+ /** A separate explicit review action; generation never calls this method. */
2846
+ acceptKeyframe(id, input, concurrency = {}) {
2847
+ return this.edit(id, { ...concurrency, ops: [{ ...input, op: "accept_keyframe_result" }] });
2848
+ }
2849
+ reconcile(id) {
2850
+ return this.client.request("POST", `${path(id)}/reconcile`, { body: {} });
2851
+ }
2710
2852
  };
2711
2853
 
2712
2854
  // src/resources/community.ts
@@ -3090,7 +3232,7 @@ var WorkspacesResource = class {
3090
3232
  return this.client.requestText("GET", `/v1/workspaces/${encodeURIComponent(id)}/usage`, { query: { ...opts, format: "csv" } });
3091
3233
  }
3092
3234
  };
3093
- var SDK_VERSION = "2.1.0" ;
3235
+ var SDK_VERSION = "2.4.0" ;
3094
3236
  var CLIENT_HEADER = "X-Nodaro-Client";
3095
3237
  var isBrowser = () => typeof window !== "undefined" && typeof window.document !== "undefined";
3096
3238
  var NodaroClient = class _NodaroClient {
@@ -3222,8 +3364,8 @@ var NodaroClient = class _NodaroClient {
3222
3364
  * stalled CSV stream aborts, it does not hang forever). Both the JSON path
3223
3365
  * ({@link request}) and the text path ({@link requestText}) go through here.
3224
3366
  */
3225
- async send(method, path, options, read) {
3226
- const url = this.buildUrl(path, options.query);
3367
+ async send(method, path2, options, read) {
3368
+ const url = this.buildUrl(path2, options.query);
3227
3369
  const token = await this.auth.getToken();
3228
3370
  const isFormData = typeof FormData !== "undefined" && options.body instanceof FormData;
3229
3371
  const headers = {
@@ -3263,14 +3405,14 @@ var NodaroClient = class _NodaroClient {
3263
3405
  }
3264
3406
  }
3265
3407
  /** Authenticated, bounded binary reads through the normal timeout/error transport. */
3266
- async requestBytes(method, path, options) {
3408
+ async requestBytes(method, path2, options) {
3267
3409
  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));
3410
+ return this.send(method, path2, options, (response) => readBinaryResponse(response, options.maxBytes));
3269
3411
  }
3270
- async request(method, path, options = {}) {
3412
+ async request(method, path2, options = {}) {
3271
3413
  return this.send(
3272
3414
  method,
3273
- path,
3415
+ path2,
3274
3416
  options,
3275
3417
  async (res) => (
3276
3418
  // 204 No Content
@@ -3283,8 +3425,8 @@ var NodaroClient = class _NodaroClient {
3283
3425
  * are still the JSON envelope and throw the same typed errors, and the read is
3284
3426
  * bounded by the same timeout as the request.
3285
3427
  */
3286
- async requestText(method, path, options = {}) {
3287
- return this.send(method, path, options, (res) => res.text());
3428
+ async requestText(method, path2, options = {}) {
3429
+ return this.send(method, path2, options, (res) => res.text());
3288
3430
  }
3289
3431
  /**
3290
3432
  * `GET /v1/me` → the authenticated user's identity (see {@link UserIdentity}).
@@ -3303,9 +3445,9 @@ var NodaroClient = class _NodaroClient {
3303
3445
  const res = await this.request("GET", "/v1/me");
3304
3446
  return res.data;
3305
3447
  }
3306
- buildUrl(path, query) {
3448
+ buildUrl(path2, query) {
3307
3449
  const base = this.baseUrl || (typeof window !== "undefined" ? window.location.origin : "http://placeholder");
3308
- const url = new URL(path, base);
3450
+ const url = new URL(path2, base);
3309
3451
  const fullUrl = this.baseUrl ? url.toString() : url.pathname + url.search;
3310
3452
  if (query) {
3311
3453
  const u = new URL(this.baseUrl ? fullUrl : fullUrl, base);