@nodaro/sdk 2.25.0 → 2.27.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
@@ -1,7 +1,16 @@
1
- import { SCENE3D_V2_LIMITS, resolveEditPlanSources, describeAudioSyncOffsetIssue, transcriptSpeakerLabels, remapTranscriptThroughEdl, WORKSPACE_HEADER } from '@nodaro/shared';
2
- export { CHARACTER_ASPECT_DEFAULTS, CHARACTER_ASPECT_OPTIONS, CHARACTER_STYLES, OBJECT_ASPECT_DEFAULTS as CREATURE_ASPECT_DEFAULTS, OBJECT_ASPECT_OPTIONS as CREATURE_ASPECT_OPTIONS, CREATURE_ATTACH_COLUMNS, LOCATION_ASSET_TYPES, LOCATION_ATTACH_COLUMNS, OBJECT_ASPECT_DEFAULTS, OBJECT_ASPECT_OPTIONS, OBJECT_ASSET_TYPES, OBJECT_ATTACH_COLUMNS, OUTPUT_BEARING_NODE_STATUSES, PROMPT_PREFIX_KEY, PROMPT_SUFFIX_KEY, RECAST_SEGMENT_PACKS, SURROUND_DIRECTIONS, USAGE_GROUP_BYS, WORKSPACE_HEADER, nodeStateMayCarryOutput, unwrapEditPlanOutput } from '@nodaro/shared';
3
- import { buildPersonHints } from '@nodaro/prompts';
4
- export { PEOPLE, PERSON_DIMENSION_LABELS, PERSON_DIMENSION_ORDER, buildPersonHints } from '@nodaro/prompts';
1
+ export { OUTPUT_BEARING_NODE_STATUSES, nodeStateMayCarryOutput } from '@nodaro/shared/workflow';
2
+ import { nodeRunRoute, NODE_TYPES_WITHOUT_RUN_ROUTE, NODE_RUN_ROUTES } from '@nodaro/shared/nodes';
3
+ export { PROMPT_PREFIX_KEY, PROMPT_SUFFIX_KEY } from '@nodaro/shared/nodes';
4
+ import { SOCIAL_POST_NODE_PLATFORM } from '@nodaro/shared/social';
5
+ import { SCENE3D_V2_LIMITS } from '@nodaro/shared/scene3d';
6
+ import { buildPersonHints } from '@nodaro/prompts/person';
7
+ export { PEOPLE, PERSON_DIMENSION_LABELS, PERSON_DIMENSION_ORDER, buildPersonHints } from '@nodaro/prompts/person';
8
+ export { CHARACTER_ASPECT_DEFAULTS, CHARACTER_ASPECT_OPTIONS, CHARACTER_STYLES, OBJECT_ASPECT_DEFAULTS as CREATURE_ASPECT_DEFAULTS, OBJECT_ASPECT_OPTIONS as CREATURE_ASPECT_OPTIONS, CREATURE_ATTACH_COLUMNS, LOCATION_ASSET_TYPES, LOCATION_ATTACH_COLUMNS, OBJECT_ASPECT_DEFAULTS, OBJECT_ASPECT_OPTIONS, OBJECT_ASSET_TYPES, OBJECT_ATTACH_COLUMNS, SURROUND_DIRECTIONS } from '@nodaro/shared/entities';
9
+ import { resolveEditPlanSources, describeAudioSyncOffsetIssue, transcriptSpeakerLabels, remapTranscriptThroughEdl } from '@nodaro/shared/edl';
10
+ export { unwrapEditPlanOutput } from '@nodaro/shared/edl';
11
+ import { WORKSPACE_HEADER } from '@nodaro/shared/organizations';
12
+ export { USAGE_GROUP_BYS, WORKSPACE_HEADER } from '@nodaro/shared/organizations';
13
+ export { RECAST_SEGMENT_PACKS } from '@nodaro/shared/apps';
5
14
 
6
15
  // src/binary-response.ts
7
16
  async function readBinaryResponse(response, maxBytes) {
@@ -112,6 +121,19 @@ var StudioOpError = class extends NodaroError {
112
121
  }
113
122
  opIndex;
114
123
  };
124
+ var CONTINUATION_REFUSAL_CODES = /* @__PURE__ */ new Set([
125
+ "already_running",
126
+ "continuation_superseded",
127
+ "render_final_already_rendered"
128
+ ]);
129
+ var ContinuationRefusedError = class extends NodaroError {
130
+ constructor(message, code, jobId) {
131
+ super(message, code, 409);
132
+ this.jobId = jobId;
133
+ this.name = "ContinuationRefusedError";
134
+ }
135
+ jobId;
136
+ };
115
137
  var JobBlockedError = class extends NodaroError {
116
138
  constructor(message = "Blocked by this deployment's content policy") {
117
139
  super(message, "job_blocked", 422);
@@ -184,12 +206,15 @@ function throwFromResponse(status, body) {
184
206
  if (status >= 400 && status < 500 && typeof body.error?.opIndex === "number") {
185
207
  throw new StudioOpError(message, code, status, body.error.opIndex);
186
208
  }
209
+ if (status === 409 && CONTINUATION_REFUSAL_CODES.has(code) && typeof body.jobId === "string") {
210
+ throw new ContinuationRefusedError(message, code, body.jobId);
211
+ }
187
212
  if (status === 422 && code === "job_blocked") throw new JobBlockedError(message);
188
213
  if (status === 403 && code === "insufficient_scope") {
189
214
  throw new ForbiddenError(message, body.error?.missingScope);
190
215
  }
191
216
  if (status === 403) throw new ForbiddenError(message);
192
- if (status === 404) throw new NotFoundError(message);
217
+ if (status === 404) throw new NotFoundError(body.error?.message ?? (typeof body.message === "string" ? body.message : void 0));
193
218
  if (status === 429) throw new RateLimitedError(message);
194
219
  if (status === 402) {
195
220
  throw new InsufficientCreditsError(message, body.error?.required, body.error?.available);
@@ -539,8 +564,6 @@ var ExecutionsResource = class {
539
564
  );
540
565
  }
541
566
  };
542
-
543
- // src/resources/nodes.ts
544
567
  var DEFAULT_POLL_MS = 2e3;
545
568
  var DEFAULT_MAX_MS = 15 * 60 * 1e3;
546
569
  var sleep = (ms, signal) => new Promise((resolve, reject) => {
@@ -558,6 +581,10 @@ var sleep = (ms, signal) => new Promise((resolve, reject) => {
558
581
  }
559
582
  signal?.addEventListener("abort", onAbort, { once: true });
560
583
  });
584
+ function notRunnable(type, why) {
585
+ return new NodaroError(`Node type "${type}" has no route of its own to run it directly: ${why}.`, "node_not_runnable", 0);
586
+ }
587
+ var isRouteNotFound = (err) => err instanceof NotFoundError && /^Route\b.*\bnot found\b/i.test(err.message);
561
588
  function extractJobId(result, label) {
562
589
  if (result && typeof result === "object" && "jobId" in result) {
563
590
  const jobId = result.jobId;
@@ -579,13 +606,20 @@ var NodesResource = class {
579
606
  return this.client.request("GET", `/v1/nodes/${encodeURIComponent(type)}`);
580
607
  }
581
608
  run(type, params = {}, options = {}) {
582
- 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)}`;
609
+ const route = type === "render-video" && typeof params.planType === "string" ? "/v1/render-video/plan" : nodeRunRoute(type);
610
+ if (!route) return Promise.reject(notRunnable(type, NODE_TYPES_WITHOUT_RUN_ROUTE[type]));
611
+ const platform = SOCIAL_POST_NODE_PLATFORM[type];
612
+ const body = platform && params.platform === void 0 ? { ...params, platform } : params;
613
+ const conventional = !Object.prototype.hasOwnProperty.call(NODE_RUN_ROUTES, type);
583
614
  return this.client.request("POST", route, {
584
- body: params,
615
+ body,
585
616
  // Transport-level retry safety, opt-in per call. The platform reads the
586
617
  // standard header, so a caller that reuses a key on a retry gets the
587
618
  // same run back instead of a second charge.
588
619
  ...options.idempotencyKey ? { headers: { "Idempotency-Key": options.idempotencyKey } } : {}
620
+ }).catch((err) => {
621
+ if (conventional && isRouteNotFound(err)) throw notRunnable(type, "it runs only inside a workflow (client.workflows.run)");
622
+ throw err;
589
623
  });
590
624
  }
591
625
  async runAndWait(type, params = {}, opts = {}) {
@@ -2386,6 +2420,20 @@ var MediaResource = class {
2386
2420
  trimAudio(input) {
2387
2421
  return this.client.request("POST", "/v1/trim-audio", { body: input });
2388
2422
  }
2423
+ /**
2424
+ * Cut a video and/or an audio file into consecutive parts
2425
+ * (`POST /v1/split-media`). Every part is at most `chunkDuration` seconds and
2426
+ * at least `minPartDuration` (default 4, never above `chunkDuration`) — a
2427
+ * short leftover is evened out with the parts before it. Video is cut on
2428
+ * whole frames with each part's sound matching its picture; audio parts are
2429
+ * sample-exact, in `audioFormat` (`mp3` default / `wav` / `aac`). With both
2430
+ * sources each is split on its own. At most 200 parts per input. Poll
2431
+ * `jobs.get(jobId)`; the parts are on the completed job's `output_data` —
2432
+ * see {@link SplitMediaOutput}.
2433
+ */
2434
+ splitMedia(input) {
2435
+ return this.client.request("POST", "/v1/split-media", { body: input });
2436
+ }
2389
2437
  /**
2390
2438
  * Turn one still image + one audio track into an MP4
2391
2439
  * (`POST /v1/still-to-video`) — locally rendered (FFmpeg), no AI model,
@@ -2513,7 +2561,10 @@ var AudioResource = class {
2513
2561
  * `ratio` (1–20, overrides `amount`), `attackMs` (1–2000, default 20) and
2514
2562
  * `releaseMs` (10–9000, default 500) are optional fine controls. A ducked mix
2515
2563
  * sums its tracks rather than averaging them, so the voice keeps its level;
2516
- * the price is the same with or without a duck.
2564
+ * the price is the same with or without a duck. `endAt` (`"longest"`, the
2565
+ * default, or `"voice"`) says where the mix ends: `"voice"` ends it when the
2566
+ * voice (the `duck.under` track) ends (needs `duck`). A video URL is a track
2567
+ * too: its audio is mixed, and a video with no sound is a silent track.
2517
2568
  */
2518
2569
  mix(input) {
2519
2570
  return this.client.request("POST", "/v1/mix-audio", { body: input });
@@ -2590,6 +2641,18 @@ var CreditsResource = class {
2590
2641
  );
2591
2642
  return res.data;
2592
2643
  }
2644
+ /**
2645
+ * `GET /v1/credits/transactions` → one page of the caller's credit
2646
+ * transactions, newest first, with `nextCursor` for the next page (`null`
2647
+ * at the end). Read `credits_charged` for what each run cost — it can be
2648
+ * below the hold (`credits_reserved`) and is `null` while the hold is open.
2649
+ * Cloud-only route: a self-hosted instance answers 404.
2650
+ */
2651
+ transactions(options = {}) {
2652
+ return this.client.request("GET", "/v1/credits/transactions", {
2653
+ query: { limit: options.limit, cursor: options.cursor }
2654
+ });
2655
+ }
2593
2656
  /**
2594
2657
  * `POST /v1/credits/model-costs` → per-identifier credit cost, for editor
2595
2658
  * cost previews. Capped at the first {@link MODEL_COSTS_LIMIT} identifiers
@@ -2601,6 +2664,23 @@ var CreditsResource = class {
2601
2664
  body: { models: ids.slice(0, MODEL_COSTS_LIMIT) }
2602
2665
  });
2603
2666
  }
2667
+ /**
2668
+ * `POST /v1/credits/claim-signup-grant` → claims the account's free signup
2669
+ * grant, once, with the browser signals of the page making the claim.
2670
+ *
2671
+ * Call it once per page load as soon as the session is known, and never
2672
+ * wait on it for rendering: it is idempotent (a decided account answers
2673
+ * with its state and `granted: false`), it costs the user nothing when it
2674
+ * fails (the next load asks again), and the signals are what make the
2675
+ * claim worth sending — collect them with `collectSignupKeys()` from
2676
+ * `@nodaro/sdk/signup-signals` and pass whatever came back, `{}` included.
2677
+ * Cloud-only route: a self-hosted instance answers 404.
2678
+ */
2679
+ claimSignupGrant(keys = {}) {
2680
+ return this.client.request("POST", "/v1/credits/claim-signup-grant", {
2681
+ body: keys
2682
+ });
2683
+ }
2604
2684
  // NOTE: no `estimate(...)` helper. The backend exposes
2605
2685
  // `POST /v1/credits/estimate-workflow` (body `{ nodes }` → `{ data: {
2606
2686
  // totalCredits, nodeCount } }`), but no consumer has a settled shape for it
@@ -3915,6 +3995,13 @@ var WorkspacesResource = class {
3915
3995
  return this.client.requestText("GET", `/v1/workspaces/${encodeURIComponent(id)}/usage`, { query: { ...opts, format: "csv" } });
3916
3996
  }
3917
3997
  };
3998
+ function tightenBody(input) {
3999
+ const { mediaUrl, ...rest } = input;
4000
+ return { mediaUrls: [mediaUrl], ...Object.fromEntries(Object.entries(rest).filter(([, v]) => v !== void 0)) };
4001
+ }
4002
+ function idempotencyHeaders(options) {
4003
+ return options?.idempotencyKey ? { headers: { "Idempotency-Key": options.idempotencyKey } } : {};
4004
+ }
3918
4005
  var EditResource = class {
3919
4006
  constructor(client) {
3920
4007
  this.client = client;
@@ -3974,6 +4061,42 @@ var EditResource = class {
3974
4061
  }
3975
4062
  });
3976
4063
  }
4064
+ /**
4065
+ * Tighten Episode (`POST /v1/tighten-episode`): tighten a podcast recording
4066
+ * in one call — transcribe, cut the silences and filler, render, with
4067
+ * optional captions, music and framing. Answers the run's job to poll; its
4068
+ * `output_data` holds the episode. The run is priced as its steps at their own
4069
+ * rates plus the listed per-run fee. Quote it first with
4070
+ * {@link estimateTightenEpisode}.
4071
+ */
4072
+ tightenEpisode(input, options) {
4073
+ return this.client.request("POST", "/v1/tighten-episode", { body: tightenBody(input), ...idempotencyHeaders(options) });
4074
+ }
4075
+ /**
4076
+ * What a Tighten Episode run is estimated at (`POST /v1/tighten-episode/estimate`):
4077
+ * its steps at their own rates plus its per-run fee. Nothing is created or
4078
+ * charged. The estimate leans high: the render is priced at the recording's
4079
+ * full length, while the charge follows the minutes actually rendered.
4080
+ */
4081
+ estimateTightenEpisode(input) {
4082
+ return this.client.request("POST", "/v1/tighten-episode/estimate", { body: tightenBody(input) });
4083
+ }
4084
+ /**
4085
+ * Continue a Tighten Episode run as a new job (`POST /v1/tighten-episode/continue`):
4086
+ * `"from-failed"` keeps the steps that finished and runs the rest (the fee is
4087
+ * held again; the failed run's was refunded); `"render-final"` renders the
4088
+ * full-quality episode after a review-on preview, with the preview's
4089
+ * settings and no fee. A refusal that names another job
4090
+ * (`already_running`, `continuation_superseded`,
4091
+ * `render_final_already_rendered`) throws a `ContinuationRefusedError`
4092
+ * carrying that `jobId`.
4093
+ */
4094
+ continueUseCase(input, options) {
4095
+ return this.client.request("POST", "/v1/tighten-episode/continue", {
4096
+ body: { jobId: input.jobId, mode: input.mode, ...input.editedEdl !== void 0 ? { editedEdl: input.editedEdl } : {} },
4097
+ ...idempotencyHeaders(options)
4098
+ });
4099
+ }
3977
4100
  /**
3978
4101
  * Plan a transcript-driven cut (`POST /v1/edit-plan`). Reads a timed
3979
4102
  * transcript (plus optional silence ranges) and the media sources, and plans
@@ -4054,7 +4177,7 @@ var EditResource = class {
4054
4177
  return remapTranscriptThroughEdl(edl, transcript);
4055
4178
  }
4056
4179
  };
4057
- var SDK_VERSION = "2.25.0" ;
4180
+ var SDK_VERSION = "2.27.0" ;
4058
4181
  var CLIENT_HEADER = "X-Nodaro-Client";
4059
4182
  var isBrowser = () => typeof window !== "undefined" && typeof window.document !== "undefined";
4060
4183
  var NodaroClient = class _NodaroClient {
@@ -4323,6 +4446,6 @@ function supabaseAuth(supabase) {
4323
4446
  };
4324
4447
  }
4325
4448
 
4326
- export { AppsResource, AudioResource, CREATURE_ASSET_TYPES, CallbackAuth, CatalogsResource, CharactersResource, CollectionsResource, CommunityResource, CompetitorsResource, CopilotResource, CreaturesResource, CreditsResource, DeveloperAppsResource, EditResource, ExecutionsResource, ForbiddenError, InsufficientCreditsError, JobAbortedError, JobBlockedError, JobFailedError, JobHeldError, JobTimeoutError, JobsResource, LibraryResource, LlmResource, LocationsResource, MediaResource, ModelsResource, NodaroClient, NodaroError, NodesResource, NotFoundError, OAuthResource, ObjectsResource, OrganizationsResource, PickerCatalogsResource, PipelinesResource, PresetsResource, ProjectsResource, PromptHelperResource, RateLimitedError, RecastResource, ReduceResource, SCENE3D_DELIVERY_ASSET_USAGE, SavedPostsResource, Scene3DResource, ShotsResource, StaticTokenAuth, StorageExceededError, StudioOpError, StudioPreviewAppliedError, StudioPreviewUnavailable, StudioProductionsResource, StudioResource, TemplatesResource, TutorialsResource, UnauthorizedError, UploadsResource, VideoProResource, VoicesResource, WorkflowConflictError, WorkflowsResource, WorkspacesResource, buildPersonSeedPrompt, createClient, isStudioGenerateEstimate, supabaseAuth, throwFromResponse };
4449
+ export { AppsResource, AudioResource, CREATURE_ASSET_TYPES, CallbackAuth, CatalogsResource, CharactersResource, CollectionsResource, CommunityResource, CompetitorsResource, ContinuationRefusedError, CopilotResource, CreaturesResource, CreditsResource, DeveloperAppsResource, EditResource, ExecutionsResource, ForbiddenError, InsufficientCreditsError, JobAbortedError, JobBlockedError, JobFailedError, JobHeldError, JobTimeoutError, JobsResource, LibraryResource, LlmResource, LocationsResource, MediaResource, ModelsResource, NodaroClient, NodaroError, NodesResource, NotFoundError, OAuthResource, ObjectsResource, OrganizationsResource, PickerCatalogsResource, PipelinesResource, PresetsResource, ProjectsResource, PromptHelperResource, RateLimitedError, RecastResource, ReduceResource, SCENE3D_DELIVERY_ASSET_USAGE, SavedPostsResource, Scene3DResource, ShotsResource, StaticTokenAuth, StorageExceededError, StudioOpError, StudioPreviewAppliedError, StudioPreviewUnavailable, StudioProductionsResource, StudioResource, TemplatesResource, TutorialsResource, UnauthorizedError, UploadsResource, VideoProResource, VoicesResource, WorkflowConflictError, WorkflowsResource, WorkspacesResource, buildPersonSeedPrompt, createClient, isStudioGenerateEstimate, supabaseAuth, throwFromResponse };
4327
4450
  //# sourceMappingURL=index.js.map
4328
4451
  //# sourceMappingURL=index.js.map