@nodaro/sdk 2.4.0 → 2.6.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
@@ -146,6 +146,20 @@ var JobAbortedError = class extends NodaroError {
146
146
  }
147
147
  jobId;
148
148
  };
149
+ var StudioPreviewUnavailable = class extends NodaroError {
150
+ constructor(message = "This deployment does not preview operation batches; nothing was sent") {
151
+ super(message, "studio_preview_unavailable", 0);
152
+ this.name = "StudioPreviewUnavailable";
153
+ }
154
+ };
155
+ var StudioPreviewAppliedError = class extends NodaroError {
156
+ constructor(applied, message = applied ? "Asked for a preview and this deployment APPLIED the batch; the change is written \u2014 see `applied`" : "Asked for a preview and this deployment answered with neither one nor a body to read; the batch may have been applied \u2014 re-read the production before deciding anything") {
157
+ super(message, "studio_preview_applied", 0);
158
+ this.applied = applied;
159
+ this.name = "StudioPreviewAppliedError";
160
+ }
161
+ applied;
162
+ };
149
163
  var JobHeldError = class extends NodaroError {
150
164
  constructor(message, jobId) {
151
165
  super(message, "job_held", 0);
@@ -2037,6 +2051,47 @@ var LlmResource = class {
2037
2051
  }
2038
2052
  };
2039
2053
 
2054
+ // src/sse.ts
2055
+ async function* readSseStream(res, opts = {}) {
2056
+ if (!res.ok) {
2057
+ let errBody = {};
2058
+ try {
2059
+ errBody = await res.json();
2060
+ } catch {
2061
+ }
2062
+ throwFromResponse(res.status, errBody);
2063
+ }
2064
+ if (!res.body) {
2065
+ throw new NodaroError(`${opts.label ?? "event stream"} has no response body`, "empty_stream", res.status);
2066
+ }
2067
+ const reader = res.body.getReader();
2068
+ const decoder = new TextDecoder();
2069
+ let buffer = "";
2070
+ try {
2071
+ for (; ; ) {
2072
+ const { done, value } = await reader.read();
2073
+ if (done) break;
2074
+ buffer += decoder.decode(value, { stream: true });
2075
+ let sep;
2076
+ while ((sep = buffer.indexOf("\n\n")) >= 0) {
2077
+ const frame = buffer.slice(0, sep);
2078
+ buffer = buffer.slice(sep + 2);
2079
+ for (const line of frame.split("\n")) {
2080
+ if (!line.startsWith("data:")) continue;
2081
+ try {
2082
+ yield JSON.parse(line.slice(5).trim());
2083
+ } catch {
2084
+ }
2085
+ }
2086
+ }
2087
+ }
2088
+ } finally {
2089
+ reader.releaseLock();
2090
+ await res.body.cancel().catch(() => {
2091
+ });
2092
+ }
2093
+ }
2094
+
2040
2095
  // src/resources/media.ts
2041
2096
  var MediaResource = class {
2042
2097
  constructor(client) {
@@ -2073,43 +2128,7 @@ var MediaResource = class {
2073
2128
  headers: token ? { Authorization: `Bearer ${token}` } : {},
2074
2129
  signal: opts.signal
2075
2130
  });
2076
- if (!res.ok) {
2077
- let errBody = {};
2078
- try {
2079
- errBody = await res.json();
2080
- } catch {
2081
- }
2082
- throwFromResponse(res.status, errBody);
2083
- }
2084
- if (!res.body) {
2085
- throw new NodaroError("progress stream has no response body", "empty_stream", res.status);
2086
- }
2087
- const reader = res.body.getReader();
2088
- const decoder = new TextDecoder();
2089
- let buffer = "";
2090
- try {
2091
- for (; ; ) {
2092
- const { done, value } = await reader.read();
2093
- if (done) break;
2094
- buffer += decoder.decode(value, { stream: true });
2095
- let sep;
2096
- while ((sep = buffer.indexOf("\n\n")) >= 0) {
2097
- const frame = buffer.slice(0, sep);
2098
- buffer = buffer.slice(sep + 2);
2099
- for (const line of frame.split("\n")) {
2100
- if (!line.startsWith("data:")) continue;
2101
- try {
2102
- yield JSON.parse(line.slice(5).trim());
2103
- } catch {
2104
- }
2105
- }
2106
- }
2107
- }
2108
- } finally {
2109
- reader.releaseLock();
2110
- await res.body.cancel().catch(() => {
2111
- });
2112
- }
2131
+ yield* readSseStream(res, { label: "progress stream" });
2113
2132
  }
2114
2133
  /**
2115
2134
  * Copy an external media URL into your Nodaro storage (`POST /v1/save-to-storage`)
@@ -2138,6 +2157,22 @@ var MediaResource = class {
2138
2157
  imageCollage(input) {
2139
2158
  return this.client.request("POST", "/v1/image-collage", { body: input });
2140
2159
  }
2160
+ /**
2161
+ * Place 1–12 layers — pictures, real text, QR codes, shapes — on a base image,
2162
+ * pixel-exactly (`POST /v1/image-overlay`) — local, deterministic, no AI.
2163
+ * Every layer position/size is a PERCENT of the base image: `anchor` (nine
2164
+ * positions, default `"center"`), `x`/`y` (offset from the anchor in % of
2165
+ * the base width/height; negative on a right/bottom anchor moves inward),
2166
+ * `width` (% of the base width, default 25 — height follows the layer's
2167
+ * aspect unless `height` is set). Plus `opacity` (0..1), `rotation`
2168
+ * (degrees), `blend` (`"over"` | `"multiply"` | `"screen"`), `fit`,
2169
+ * `shadow` and `roundedCorners`. The output keeps the base's pixel size
2170
+ * unless `canvas` is set. Flat credit cost. Poll `jobs.get(jobId)` for the
2171
+ * finished image.
2172
+ */
2173
+ imageOverlay(input) {
2174
+ return this.client.request("POST", "/v1/image-overlay", { body: input });
2175
+ }
2141
2176
  /**
2142
2177
  * Trim a video to a range (`POST /v1/trim-video`). Give the range in whichever
2143
2178
  * unit fits: `startTime`/`endTime` seconds, `trim*Frames`, `trim*Seconds`, or
@@ -2631,19 +2666,27 @@ var StudioProductionsResource = class {
2631
2666
  );
2632
2667
  return res.data;
2633
2668
  }
2634
- /**
2635
- * Apply a batch of operations. Atomic: one bad operation refuses the whole
2636
- * batch with a `StudioOpError` naming its index, and nothing is written. On
2637
- * success adopt `production` wholesale and carry `version` forward as the
2638
- * next `baseVersion`.
2639
- */
2640
2669
  async ops(productionId, input) {
2641
- const res = await this.client.request(
2670
+ if (input.dryRun !== true) {
2671
+ const res2 = await this.client.request(
2672
+ "POST",
2673
+ this.path(productionId, "/ops"),
2674
+ { body: input }
2675
+ );
2676
+ return res2.data;
2677
+ }
2678
+ const ping = await this.client.request(
2642
2679
  "POST",
2643
2680
  this.path(productionId, "/ops"),
2644
- { body: input }
2681
+ { body: { ops: [], dryRun: true } }
2645
2682
  );
2646
- return res.data;
2683
+ if (ping?.data?.dryRun !== true) throw new StudioPreviewUnavailable();
2684
+ const res = await this.client.request("POST", this.path(productionId, "/ops"), { body: input });
2685
+ const data = res?.data;
2686
+ if (data?.dryRun !== true) {
2687
+ throw new StudioPreviewAppliedError(data);
2688
+ }
2689
+ return data;
2647
2690
  }
2648
2691
  /**
2649
2692
  * Land every generation that has finished since you last looked, and report
@@ -2851,6 +2894,103 @@ var StudioResource = class {
2851
2894
  }
2852
2895
  };
2853
2896
 
2897
+ // src/resources/copilot.ts
2898
+ var COPILOT_FRAME_TYPES = /* @__PURE__ */ new Set([
2899
+ "metadata",
2900
+ "token",
2901
+ "tool_call",
2902
+ "workflow_updated",
2903
+ "workflow_created",
2904
+ "run_proposed",
2905
+ "memory_saved",
2906
+ "usage",
2907
+ "done",
2908
+ "error"
2909
+ ]);
2910
+ function isCopilotFrame(value) {
2911
+ if (typeof value !== "object" || value === null) return false;
2912
+ const type = value.type;
2913
+ return typeof type === "string" && COPILOT_FRAME_TYPES.has(type);
2914
+ }
2915
+ var CopilotResource = class {
2916
+ constructor(client) {
2917
+ this.client = client;
2918
+ }
2919
+ client;
2920
+ /**
2921
+ * Open a conversation (`POST /v1/copilot/threads`) — on `workflowId`, or on
2922
+ * a workflow the server creates from `prompt`. Re-opening a workflow that
2923
+ * already has an active thread answers THAT thread rather than a second one.
2924
+ */
2925
+ create(input) {
2926
+ return this.client.request("POST", "/v1/copilot/threads", { body: input });
2927
+ }
2928
+ /**
2929
+ * The active thread for a workflow (`GET /v1/copilot/threads?workflowId=…`),
2930
+ * or `null` when the user has none open on it.
2931
+ */
2932
+ list(params) {
2933
+ return this.client.request("GET", "/v1/copilot/threads", { query: { workflowId: params.workflowId } });
2934
+ }
2935
+ /**
2936
+ * One thread with its messages (`GET /v1/copilot/threads/:id`). `after` reads
2937
+ * only what followed that sequence number (the panel's catch-up); `limit`
2938
+ * caps the page at the server's own ceiling.
2939
+ */
2940
+ get(id, opts = {}) {
2941
+ return this.client.request("GET", `/v1/copilot/threads/${encodeURIComponent(id)}`, {
2942
+ query: { ...opts.after !== void 0 ? { after: opts.after } : {}, ...opts.limit !== void 0 ? { limit: opts.limit } : {} }
2943
+ });
2944
+ }
2945
+ /**
2946
+ * Close a conversation (`DELETE /v1/copilot/threads/:id`). Archival, not
2947
+ * deletion — the messages stay readable. Refused while a turn is running.
2948
+ */
2949
+ archive(id) {
2950
+ return this.client.request("DELETE", `/v1/copilot/threads/${encodeURIComponent(id)}`);
2951
+ }
2952
+ /**
2953
+ * Stop the running turn (`POST /v1/copilot/threads/:id/cancel`). Answers the
2954
+ * turn it asked to stop; the turn's own stream ends with a `done` frame.
2955
+ */
2956
+ cancel(id) {
2957
+ return this.client.request("POST", `/v1/copilot/threads/${encodeURIComponent(id)}/cancel`);
2958
+ }
2959
+ /**
2960
+ * Send one message and iterate the turn's frames
2961
+ * (`POST /v1/copilot/threads/:id/messages`, server-sent events).
2962
+ *
2963
+ * Deliberately NOT routed through `client.request`: that path arms an abort
2964
+ * timer around the whole exchange, and a turn legitimately runs for minutes —
2965
+ * inheriting `timeoutMs` would cut the assistant off mid-answer. The caller
2966
+ * owns the lifetime instead: pass `signal` to end the stream, or stop
2967
+ * iterating (the reader cancels the body, which ends the request).
2968
+ *
2969
+ * A frame kind this package does not model is skipped rather than thrown, so
2970
+ * a newer server cannot break an older caller.
2971
+ */
2972
+ async *stream(threadId, opts) {
2973
+ const url = `${this.client.baseUrl}/v1/copilot/threads/${encodeURIComponent(threadId)}/messages`;
2974
+ const token = await this.client.auth.getToken();
2975
+ const res = await this.client.fetch(url, {
2976
+ method: "POST",
2977
+ headers: {
2978
+ "Content-Type": "application/json",
2979
+ ...token ? { Authorization: `Bearer ${token}` } : {}
2980
+ },
2981
+ body: JSON.stringify({
2982
+ message: opts.message,
2983
+ ...opts.baseVersion !== void 0 ? { baseVersion: opts.baseVersion } : {},
2984
+ ...opts.tier ? { tier: opts.tier } : {}
2985
+ }),
2986
+ signal: opts.signal
2987
+ });
2988
+ for await (const value of readSseStream(res, { label: "copilot stream" })) {
2989
+ if (isCopilotFrame(value)) yield value;
2990
+ }
2991
+ }
2992
+ };
2993
+
2854
2994
  // src/resources/community.ts
2855
2995
  var CommunityResource = class {
2856
2996
  constructor(client) {
@@ -3232,7 +3372,7 @@ var WorkspacesResource = class {
3232
3372
  return this.client.requestText("GET", `/v1/workspaces/${encodeURIComponent(id)}/usage`, { query: { ...opts, format: "csv" } });
3233
3373
  }
3234
3374
  };
3235
- var SDK_VERSION = "2.4.0" ;
3375
+ var SDK_VERSION = "2.6.0" ;
3236
3376
  var CLIENT_HEADER = "X-Nodaro-Client";
3237
3377
  var isBrowser = () => typeof window !== "undefined" && typeof window.document !== "undefined";
3238
3378
  var NodaroClient = class _NodaroClient {
@@ -3284,6 +3424,7 @@ var NodaroClient = class _NodaroClient {
3284
3424
  shots;
3285
3425
  recast;
3286
3426
  studio;
3427
+ copilot;
3287
3428
  community;
3288
3429
  templates;
3289
3430
  tutorials;
@@ -3330,6 +3471,7 @@ var NodaroClient = class _NodaroClient {
3330
3471
  this.shots = new ShotsResource(this);
3331
3472
  this.recast = new RecastResource(this);
3332
3473
  this.studio = new StudioResource(this);
3474
+ this.copilot = new CopilotResource(this);
3333
3475
  this.community = new CommunityResource(this);
3334
3476
  this.templates = new TemplatesResource(this);
3335
3477
  this.tutorials = new TutorialsResource(this);
@@ -3582,6 +3724,7 @@ exports.CallbackAuth = CallbackAuth;
3582
3724
  exports.CatalogsResource = CatalogsResource;
3583
3725
  exports.CharactersResource = CharactersResource;
3584
3726
  exports.CommunityResource = CommunityResource;
3727
+ exports.CopilotResource = CopilotResource;
3585
3728
  exports.CreaturesResource = CreaturesResource;
3586
3729
  exports.CreditsResource = CreditsResource;
3587
3730
  exports.DeveloperAppsResource = DeveloperAppsResource;
@@ -3619,6 +3762,8 @@ exports.ShotsResource = ShotsResource;
3619
3762
  exports.StaticTokenAuth = StaticTokenAuth;
3620
3763
  exports.StorageExceededError = StorageExceededError;
3621
3764
  exports.StudioOpError = StudioOpError;
3765
+ exports.StudioPreviewAppliedError = StudioPreviewAppliedError;
3766
+ exports.StudioPreviewUnavailable = StudioPreviewUnavailable;
3622
3767
  exports.StudioProductionsResource = StudioProductionsResource;
3623
3768
  exports.StudioResource = StudioResource;
3624
3769
  exports.TemplatesResource = TemplatesResource;