@nodaro/sdk 1.1.1
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/LICENSE +202 -0
- package/README.md +296 -0
- package/dist/index.cjs +2085 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +4047 -0
- package/dist/index.d.ts +4047 -0
- package/dist/index.js +1977 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4047 @@
|
|
|
1
|
+
import { GenericNode, GenericEdge, WorkflowExport, ConnectedReference, TtsProvider, EntityStyle, CharacterAspectRatio, CharacterAttachColumn, LocationAssetType, LocationAttachColumn, SurroundDirection, ObjectAssetType, ObjectAttachColumn, ObjectAspectRatio, CreatureAttachColumn, PipelineInput, PipelineStatus, PipelineMode, PipelineStageName, SubGateName, ChatEnabledStage, ProposedChange, ReduceStrategyId, ReduceMeta, Voice, VoiceLibraryParams, VoiceLibraryResponse, VoiceClone, AudioFxPreset, BrowseCommunityParams, BrowseCommunityResult, CommunityCard, CommunityFullDetail, CommunityEntityType, CloneListingResult, FavoriteListingResult, CommunityReportReason, ReportListingResult, PublishListingParams, PublishListingResult, SharedListing } from '@nodaro/shared';
|
|
2
|
+
export { BrowseCommunityParams, BrowseCommunityResult, 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, CharacterAspectRatio, CloneListingResult, CommunityCard, CommunityEntityType, CommunityReportReason, CommunitySort, ConnectedReference, ObjectAspectRatio as CreatureAspectRatio, CreatureAttachColumn, EntityStyle, FavoriteListingResult, GenericEdge, GenericNode, LOCATION_ASSET_TYPES, LOCATION_ATTACH_COLUMNS, LocationAssetType, LocationAttachColumn, OBJECT_ASPECT_DEFAULTS, OBJECT_ASPECT_OPTIONS, OBJECT_ASSET_TYPES, OBJECT_ATTACH_COLUMNS, ObjectAspectRatio, ObjectAssetType, ObjectAttachColumn, PipelineStageName, PublishListingParams, PublishListingResult, ReduceMeta, ReduceStrategyId, ReportListingResult, SURROUND_DIRECTIONS, SharedListing, SharedVoice, SurroundDirection, Voice, VoiceClone, VoiceLibraryParams, VoiceLibraryResponse, WorkflowExport, WorkflowExportCharacter, WorkflowExportLocation, WorkflowExportObject } from '@nodaro/shared';
|
|
3
|
+
import { PersonValue, WizardNodeContext, WizardQuestion, WizardSelection, RecommendedModel, FactoryPreset } from '@nodaro/prompts';
|
|
4
|
+
export { PEOPLE, PERSON_DIMENSION_LABELS, PERSON_DIMENSION_ORDER, PersonValue, RecommendedModel, WizardNodeContext, WizardOption, WizardQuestion, WizardSelection, buildPersonHints } from '@nodaro/prompts';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Auth provides the token used for `Authorization: Bearer <token>` on each request.
|
|
8
|
+
* Implementations:
|
|
9
|
+
* - StaticTokenAuth — fixed string (server-side, OAuth access token, API token)
|
|
10
|
+
* - CallbackAuth — caller-supplied async function (BYO logic)
|
|
11
|
+
* - supabaseAuth — pulls JWT from a Supabase client live (browser frontends)
|
|
12
|
+
*/
|
|
13
|
+
interface Auth {
|
|
14
|
+
/** Returns the current Bearer token, or null if not authenticated. */
|
|
15
|
+
getToken(): Promise<string | null>;
|
|
16
|
+
}
|
|
17
|
+
declare class StaticTokenAuth implements Auth {
|
|
18
|
+
private token;
|
|
19
|
+
constructor(token: string);
|
|
20
|
+
getToken(): Promise<string>;
|
|
21
|
+
}
|
|
22
|
+
declare class CallbackAuth implements Auth {
|
|
23
|
+
private fn;
|
|
24
|
+
constructor(fn: () => string | null | Promise<string | null>);
|
|
25
|
+
getToken(): Promise<string | null>;
|
|
26
|
+
}
|
|
27
|
+
interface SupabaseLikeClient {
|
|
28
|
+
auth: {
|
|
29
|
+
getSession(): Promise<{
|
|
30
|
+
data: {
|
|
31
|
+
session: {
|
|
32
|
+
access_token: string;
|
|
33
|
+
} | null;
|
|
34
|
+
};
|
|
35
|
+
}>;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** Pulls a JWT from a Supabase v2 client. Caller supplies their own supabase. */
|
|
39
|
+
declare function supabaseAuth(supabase: SupabaseLikeClient): Auth;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Workflow metadata + (when fetched as a single record) full nodes/edges/settings.
|
|
43
|
+
*
|
|
44
|
+
* The list endpoint returns metadata only; `get`, `create`, and `update` return the
|
|
45
|
+
* full record. `nodes`, `edges`, `settings`, and `sourcePrompt` are present only on
|
|
46
|
+
* full records and omitted in list responses.
|
|
47
|
+
*/
|
|
48
|
+
interface Workflow {
|
|
49
|
+
id: string;
|
|
50
|
+
projectId: string | null;
|
|
51
|
+
userId: string;
|
|
52
|
+
name: string;
|
|
53
|
+
description?: string | null;
|
|
54
|
+
folderId?: string | null;
|
|
55
|
+
isTemplate?: boolean;
|
|
56
|
+
version?: number;
|
|
57
|
+
thumbnailUrl?: string | null;
|
|
58
|
+
nodes?: GenericNode[];
|
|
59
|
+
edges?: GenericEdge[];
|
|
60
|
+
settings?: Record<string, unknown>;
|
|
61
|
+
sourcePrompt?: string | null;
|
|
62
|
+
createdAt: string;
|
|
63
|
+
updatedAt: string;
|
|
64
|
+
}
|
|
65
|
+
interface ListWorkflowsParams {
|
|
66
|
+
/** Required — list endpoint is `/v1/projects/:projectId/workflows`. */
|
|
67
|
+
projectId: string;
|
|
68
|
+
}
|
|
69
|
+
interface CreateWorkflowInput {
|
|
70
|
+
/** Required — workflow is created under this project. */
|
|
71
|
+
projectId: string;
|
|
72
|
+
name: string;
|
|
73
|
+
description?: string;
|
|
74
|
+
folderId?: string | null;
|
|
75
|
+
nodes?: GenericNode[];
|
|
76
|
+
edges?: GenericEdge[];
|
|
77
|
+
settings?: Record<string, unknown>;
|
|
78
|
+
sourcePrompt?: string;
|
|
79
|
+
}
|
|
80
|
+
interface UpdateWorkflowInput {
|
|
81
|
+
name?: string;
|
|
82
|
+
description?: string;
|
|
83
|
+
folderId?: string | null;
|
|
84
|
+
nodes?: GenericNode[];
|
|
85
|
+
edges?: GenericEdge[];
|
|
86
|
+
settings?: Record<string, unknown>;
|
|
87
|
+
sourcePrompt?: string;
|
|
88
|
+
thumbnailUrl?: string | null;
|
|
89
|
+
}
|
|
90
|
+
interface RunWorkflowParams {
|
|
91
|
+
/** Optional subset of node IDs to execute. Omit to run the full workflow. */
|
|
92
|
+
nodeIds?: string[];
|
|
93
|
+
}
|
|
94
|
+
interface RunWorkflowResult {
|
|
95
|
+
executionId: string;
|
|
96
|
+
status: "pending" | "running";
|
|
97
|
+
}
|
|
98
|
+
declare class WorkflowsResource {
|
|
99
|
+
private client;
|
|
100
|
+
constructor(client: NodaroClient);
|
|
101
|
+
/** List workflows for a project. Returns metadata only — `nodes`/`edges` are not included. */
|
|
102
|
+
list(params: ListWorkflowsParams): Promise<{
|
|
103
|
+
data: Workflow[];
|
|
104
|
+
}>;
|
|
105
|
+
/** Get a workflow including its full nodes/edges/settings. */
|
|
106
|
+
get(id: string): Promise<{
|
|
107
|
+
data: Workflow;
|
|
108
|
+
}>;
|
|
109
|
+
/**
|
|
110
|
+
* Get a PUBLICLY-SHARED workflow by id (`GET /v1/public/workflows/:id`) — the
|
|
111
|
+
* unauthenticated share-by-link read. Returns the workflow's nodes/edges/
|
|
112
|
+
* settings ONLY when it's opted into sharing server-side (`settings.studio.shared
|
|
113
|
+
* === true`); otherwise the route 404s (→ `NotFoundError`). No auth required —
|
|
114
|
+
* a share viewer has no session; the SDK omits the bearer when no token exists.
|
|
115
|
+
*/
|
|
116
|
+
getPublic(id: string): Promise<{
|
|
117
|
+
data: Workflow;
|
|
118
|
+
}>;
|
|
119
|
+
/**
|
|
120
|
+
* Create a workflow under a project. Returns the full record.
|
|
121
|
+
* NOTE: server route is `POST /v1/projects/:projectId/workflows`.
|
|
122
|
+
*/
|
|
123
|
+
create(input: CreateWorkflowInput): Promise<{
|
|
124
|
+
data: Workflow;
|
|
125
|
+
}>;
|
|
126
|
+
/** Patch a workflow. Returns the full updated record. */
|
|
127
|
+
update(id: string, input: UpdateWorkflowInput): Promise<{
|
|
128
|
+
data: Workflow;
|
|
129
|
+
}>;
|
|
130
|
+
/** Delete a workflow. Returns `{ success: true }`. */
|
|
131
|
+
delete(id: string): Promise<{
|
|
132
|
+
success: true;
|
|
133
|
+
}>;
|
|
134
|
+
/**
|
|
135
|
+
* Run a workflow. Returns the executionId for polling via
|
|
136
|
+
* `client.executions.get(executionId)`.
|
|
137
|
+
*/
|
|
138
|
+
run(id: string, params?: RunWorkflowParams): Promise<RunWorkflowResult>;
|
|
139
|
+
/**
|
|
140
|
+
* Export a workflow as a portable JSON bundle.
|
|
141
|
+
* Pass `opts.assets = true` to include character/object/location entity data.
|
|
142
|
+
*/
|
|
143
|
+
export(workflowId: string, opts?: {
|
|
144
|
+
assets?: boolean;
|
|
145
|
+
}): Promise<{
|
|
146
|
+
data: WorkflowExport;
|
|
147
|
+
}>;
|
|
148
|
+
/**
|
|
149
|
+
* Import a `WorkflowExport` bundle into the specified project.
|
|
150
|
+
* Re-creates any bundled assets (characters, objects, locations) under your account.
|
|
151
|
+
*/
|
|
152
|
+
import(input: WorkflowExport & {
|
|
153
|
+
projectId: string;
|
|
154
|
+
}): Promise<{
|
|
155
|
+
data: Workflow;
|
|
156
|
+
}>;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
interface Project {
|
|
160
|
+
id: string;
|
|
161
|
+
userId: string;
|
|
162
|
+
name: string;
|
|
163
|
+
description?: string | null;
|
|
164
|
+
settings?: Record<string, unknown>;
|
|
165
|
+
createdAt: string;
|
|
166
|
+
updatedAt: string;
|
|
167
|
+
}
|
|
168
|
+
interface CreateProjectInput {
|
|
169
|
+
name: string;
|
|
170
|
+
description?: string;
|
|
171
|
+
settings?: Record<string, unknown>;
|
|
172
|
+
}
|
|
173
|
+
interface UpdateProjectInput {
|
|
174
|
+
name?: string;
|
|
175
|
+
description?: string;
|
|
176
|
+
settings?: Record<string, unknown>;
|
|
177
|
+
}
|
|
178
|
+
declare class ProjectsResource {
|
|
179
|
+
private client;
|
|
180
|
+
constructor(client: NodaroClient);
|
|
181
|
+
/** List the authenticated user's projects. */
|
|
182
|
+
list(): Promise<{
|
|
183
|
+
data: Project[];
|
|
184
|
+
}>;
|
|
185
|
+
/** Get a project by ID. */
|
|
186
|
+
get(id: string): Promise<{
|
|
187
|
+
data: Project;
|
|
188
|
+
}>;
|
|
189
|
+
/** Create a new project. */
|
|
190
|
+
create(input: CreateProjectInput): Promise<{
|
|
191
|
+
data: Project;
|
|
192
|
+
}>;
|
|
193
|
+
/** Update a project. At least one field must be provided. */
|
|
194
|
+
update(id: string, input: UpdateProjectInput): Promise<{
|
|
195
|
+
data: Project;
|
|
196
|
+
}>;
|
|
197
|
+
/** Delete a project. Returns `{ success: true }`. */
|
|
198
|
+
delete(id: string): Promise<{
|
|
199
|
+
success: true;
|
|
200
|
+
}>;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
type JobStatus = "pending" | "queued" | "processing" | "completed" | "failed" | "cancelled";
|
|
204
|
+
/**
|
|
205
|
+
* Job record returned to non-admin SDK consumers. Field names are snake_case
|
|
206
|
+
* to match the wire format (per the OpenAPI fix in Phase 1).
|
|
207
|
+
*
|
|
208
|
+
* Sensitive fields stripped server-side for non-admin callers:
|
|
209
|
+
* `provider`, `provider_cost`, `display_cost`, `credits_actual`. USD
|
|
210
|
+
* pricing is admin-only across api/sdk/mcp — non-admin consumers see
|
|
211
|
+
* only the `credits` abstraction.
|
|
212
|
+
*/
|
|
213
|
+
interface Job {
|
|
214
|
+
id: string;
|
|
215
|
+
status: JobStatus;
|
|
216
|
+
progress: number;
|
|
217
|
+
user_id: string;
|
|
218
|
+
input_data: unknown;
|
|
219
|
+
output_data: unknown;
|
|
220
|
+
error_message: string | null;
|
|
221
|
+
credits: number | null;
|
|
222
|
+
job_type: string | null;
|
|
223
|
+
created_at: string;
|
|
224
|
+
started_at: string | null;
|
|
225
|
+
completed_at: string | null;
|
|
226
|
+
}
|
|
227
|
+
interface CancelJobResult {
|
|
228
|
+
success: true;
|
|
229
|
+
cancelled: number;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Lean job status returned by `GET /v1/jobs/:id/status`. Skips the
|
|
233
|
+
* `input_data` JSONB, cost/timestamp columns, and the public sanitize pass —
|
|
234
|
+
* intended for poll loops that only need progress/output/error.
|
|
235
|
+
*/
|
|
236
|
+
interface JobStatusResult {
|
|
237
|
+
id: string;
|
|
238
|
+
status: JobStatus;
|
|
239
|
+
progress?: number;
|
|
240
|
+
output_data?: unknown;
|
|
241
|
+
error_message?: string | null;
|
|
242
|
+
}
|
|
243
|
+
declare class JobsResource {
|
|
244
|
+
private client;
|
|
245
|
+
constructor(client: NodaroClient);
|
|
246
|
+
/** Get a single job by ID. */
|
|
247
|
+
get(id: string): Promise<{
|
|
248
|
+
data: Job;
|
|
249
|
+
}>;
|
|
250
|
+
/**
|
|
251
|
+
* Get the lean status of a single job (poll-loop friendly).
|
|
252
|
+
* Hits `GET /v1/jobs/:id/status` — returns only id/status/progress/
|
|
253
|
+
* output_data/error_message, with far less wire/CPU cost than `get()`.
|
|
254
|
+
* Same auth + ownership semantics as {@link get}.
|
|
255
|
+
*/
|
|
256
|
+
getStatus(id: string): Promise<{
|
|
257
|
+
data: JobStatusResult;
|
|
258
|
+
}>;
|
|
259
|
+
/**
|
|
260
|
+
* Cancel a job. Server route is `POST /v1/jobs/:jobId/cancel`.
|
|
261
|
+
* Refunds any reserved credit holds.
|
|
262
|
+
*/
|
|
263
|
+
cancel(id: string): Promise<CancelJobResult>;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
type ExecutionStatus = "pending" | "running" | "completed" | "failed" | "cancelled" | "stopping" | "timed_out" | "discarded";
|
|
267
|
+
type ExecutionTriggerType = "manual" | "webhook" | "schedule" | "app_run" | "single-node";
|
|
268
|
+
/**
|
|
269
|
+
* Per-node state inside an execution's `nodeStates` map. Keys are node IDs.
|
|
270
|
+
*
|
|
271
|
+
* Shape mirrors `services/workflow-engine/types.ts` plus the synthetic
|
|
272
|
+
* single-node-job shape from `routes/workflow-execution.ts`.
|
|
273
|
+
*/
|
|
274
|
+
interface NodeExecutionState {
|
|
275
|
+
status: string;
|
|
276
|
+
nodeType?: string;
|
|
277
|
+
jobId?: string | null;
|
|
278
|
+
creditsUsed?: number;
|
|
279
|
+
error?: string | null;
|
|
280
|
+
startedAt?: string | null;
|
|
281
|
+
completedAt?: string | null;
|
|
282
|
+
[key: string]: unknown;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Workflow execution record. Returned by `get()` and `cancel()` (the cancel
|
|
286
|
+
* endpoint returns `{ success: true }`, not the execution itself).
|
|
287
|
+
*/
|
|
288
|
+
interface WorkflowExecution {
|
|
289
|
+
id: string;
|
|
290
|
+
workflowId: string | null;
|
|
291
|
+
userId: string;
|
|
292
|
+
status: ExecutionStatus;
|
|
293
|
+
triggerType: ExecutionTriggerType;
|
|
294
|
+
triggerData?: unknown;
|
|
295
|
+
nodeStates: Record<string, NodeExecutionState>;
|
|
296
|
+
totalNodes: number;
|
|
297
|
+
completedNodes: number;
|
|
298
|
+
failedNodes: number;
|
|
299
|
+
totalCreditsUsed: number;
|
|
300
|
+
errorMessage: string | null;
|
|
301
|
+
startedAt: string | null;
|
|
302
|
+
completedAt: string | null;
|
|
303
|
+
createdAt: string;
|
|
304
|
+
updatedAt: string;
|
|
305
|
+
}
|
|
306
|
+
/** Summary returned by `listForWorkflow()`. Excludes per-row `triggerData`/`updatedAt`. */
|
|
307
|
+
interface WorkflowExecutionSummary {
|
|
308
|
+
id: string;
|
|
309
|
+
status: ExecutionStatus;
|
|
310
|
+
triggerType: ExecutionTriggerType;
|
|
311
|
+
nodeStates: Record<string, NodeExecutionState>;
|
|
312
|
+
totalNodes: number;
|
|
313
|
+
completedNodes: number;
|
|
314
|
+
failedNodes: number;
|
|
315
|
+
totalCreditsUsed: number;
|
|
316
|
+
errorMessage: string | null;
|
|
317
|
+
startedAt: string | null;
|
|
318
|
+
completedAt: string | null;
|
|
319
|
+
createdAt: string;
|
|
320
|
+
}
|
|
321
|
+
interface ListExecutionsForWorkflowParams {
|
|
322
|
+
limit?: number;
|
|
323
|
+
cursor?: string;
|
|
324
|
+
/** Comma-separated list of statuses, e.g. "pending,running". */
|
|
325
|
+
status?: string;
|
|
326
|
+
/** "editor" excludes app_run / component / webhook / schedule executions. */
|
|
327
|
+
source?: "editor" | "all";
|
|
328
|
+
}
|
|
329
|
+
interface ListExecutionsPage<T> {
|
|
330
|
+
data: T[];
|
|
331
|
+
nextCursor?: string;
|
|
332
|
+
}
|
|
333
|
+
interface CancelExecutionParams {
|
|
334
|
+
/**
|
|
335
|
+
* "after_current" sets the execution to "stopping" (let in-flight nodes
|
|
336
|
+
* finish, then stop). Default behavior cancels immediately.
|
|
337
|
+
*/
|
|
338
|
+
mode?: "after_current" | "discard";
|
|
339
|
+
}
|
|
340
|
+
declare class ExecutionsResource {
|
|
341
|
+
private client;
|
|
342
|
+
constructor(client: NodaroClient);
|
|
343
|
+
/** Get an execution by ID. Falls back to standalone single-node jobs server-side. */
|
|
344
|
+
get(id: string): Promise<{
|
|
345
|
+
data: WorkflowExecution;
|
|
346
|
+
}>;
|
|
347
|
+
/** List executions for a workflow. Merges workflow_executions + standalone single-node jobs. */
|
|
348
|
+
listForWorkflow(workflowId: string, params?: ListExecutionsForWorkflowParams): Promise<ListExecutionsPage<WorkflowExecutionSummary>>;
|
|
349
|
+
/** Cancel an execution. Returns `{ success: true }`. */
|
|
350
|
+
cancel(id: string, params?: CancelExecutionParams): Promise<{
|
|
351
|
+
success: true;
|
|
352
|
+
}>;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
type NodeCategory = "input" | "parameter" | "ai-image" | "ai-video" | "ai-audio" | "ai-text" | "processing" | "composition" | "trigger" | "output" | "control" | "entity" | "utility";
|
|
356
|
+
type OutputType = "text" | "image" | "video" | "audio" | "data" | "none";
|
|
357
|
+
/**
|
|
358
|
+
* Field shape inside a node's `inputSchema.fields[]`. Mirrors
|
|
359
|
+
* `backend/src/lib/node-registry.ts`.
|
|
360
|
+
*/
|
|
361
|
+
interface NodeInputField {
|
|
362
|
+
key: string;
|
|
363
|
+
type: string;
|
|
364
|
+
required?: boolean;
|
|
365
|
+
options?: string[];
|
|
366
|
+
}
|
|
367
|
+
interface NodeInputSchema {
|
|
368
|
+
fields: NodeInputField[];
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Node descriptor returned by `GET /v1/nodes` and `GET /v1/nodes/:type`.
|
|
372
|
+
* Mirrors `backend/src/lib/node-registry.ts#NodeDescriptor`.
|
|
373
|
+
*/
|
|
374
|
+
interface NodeDescriptor {
|
|
375
|
+
type: string;
|
|
376
|
+
label: string;
|
|
377
|
+
category: NodeCategory;
|
|
378
|
+
description: string;
|
|
379
|
+
outputType: OutputType;
|
|
380
|
+
/** Credit cost. Number when fixed, string range like "1-8" when model-dependent, undefined if free. */
|
|
381
|
+
creditCost?: number | string;
|
|
382
|
+
/** Input fields the node exposes for user override (subset of full config). */
|
|
383
|
+
inputSchema?: NodeInputSchema;
|
|
384
|
+
/** For AI nodes: list of provider IDs supported. */
|
|
385
|
+
providers?: string[];
|
|
386
|
+
/** Capability flags such as "supports-reference-image" or "supports-end-frame". */
|
|
387
|
+
capabilities?: string[];
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Result of a direct node execution. Most node types return `{ jobId }` and
|
|
391
|
+
* are processed asynchronously by a worker — the caller polls
|
|
392
|
+
* `client.jobs.get(jobId)` until status is `completed`/`failed`.
|
|
393
|
+
*
|
|
394
|
+
* A small subset (combine-text, split-text, composite — the "inline"
|
|
395
|
+
* orchestrator categories) execute synchronously and return their full
|
|
396
|
+
* result body. The shape is route-specific; consumers should branch on the
|
|
397
|
+
* presence of `jobId`.
|
|
398
|
+
*/
|
|
399
|
+
type RunNodeResult = {
|
|
400
|
+
jobId: string;
|
|
401
|
+
usageLogId?: string;
|
|
402
|
+
[k: string]: unknown;
|
|
403
|
+
} | Record<string, unknown>;
|
|
404
|
+
/**
|
|
405
|
+
* Structured references — the editor's wired-reference shape — shared by
|
|
406
|
+
* `generate-image` and `generate-video`. The route assembles them server-side
|
|
407
|
+
* into per-ref `@image_N` directives and resolves `{image:N}` prompt tokens, so
|
|
408
|
+
* a direct SDK run binds inline references exactly like the canvas. Pass the
|
|
409
|
+
* same `ConnectedReference[]` the editor builds; the route dedupes + caps them
|
|
410
|
+
* to the provider's image-reference limit.
|
|
411
|
+
*/
|
|
412
|
+
interface StructuredReferenceParams {
|
|
413
|
+
/** Wired references assembled server-side (deduped + capped per provider). */
|
|
414
|
+
connectedReferences?: ConnectedReference[];
|
|
415
|
+
/** Reorder the assembled reference list by stable ref ids; renumbers `@image_N`. */
|
|
416
|
+
referenceOrder?: string[];
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Typed request body for `nodes.run("generate-image", …)` / `runAndWait`.
|
|
420
|
+
* Common fields are typed; any other route field passes through via the index
|
|
421
|
+
* signature (the route Zod-validates the full body).
|
|
422
|
+
*/
|
|
423
|
+
interface GenerateImageParams extends StructuredReferenceParams {
|
|
424
|
+
prompt?: string;
|
|
425
|
+
provider?: string;
|
|
426
|
+
/** Flat reference image URLs — appended after `connectedReferences`. */
|
|
427
|
+
referenceImageUrls?: string[];
|
|
428
|
+
negativePrompt?: string;
|
|
429
|
+
[k: string]: unknown;
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Typed request body for `nodes.run("generate-video", …)` / `runAndWait`.
|
|
433
|
+
* Common fields are typed; any other route field passes through.
|
|
434
|
+
*/
|
|
435
|
+
interface GenerateVideoParams extends StructuredReferenceParams {
|
|
436
|
+
prompt?: string;
|
|
437
|
+
provider?: string;
|
|
438
|
+
/** Start-frame image (image-to-video). */
|
|
439
|
+
imageUrl?: string;
|
|
440
|
+
referenceImageUrls?: string[];
|
|
441
|
+
referenceVideoUrls?: string[];
|
|
442
|
+
referenceAudioUrls?: string[];
|
|
443
|
+
[k: string]: unknown;
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Typed request body for `nodes.run("assemble-narrated-video", …)` / `runAndWait`.
|
|
447
|
+
* Assembles blocks of video with audio narration into a single composed video.
|
|
448
|
+
*/
|
|
449
|
+
interface AssembleNarratedVideoParams {
|
|
450
|
+
blocks: {
|
|
451
|
+
videoUrl: string;
|
|
452
|
+
audioUrl?: string;
|
|
453
|
+
}[];
|
|
454
|
+
voiceVolume?: number;
|
|
455
|
+
clipAudioVolume?: number;
|
|
456
|
+
maxSlowdown?: number;
|
|
457
|
+
trimStartFrames?: number;
|
|
458
|
+
trimEndFrames?: number;
|
|
459
|
+
[k: string]: unknown;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* The `output_data` shape a finalized generation job writes. Every async
|
|
463
|
+
* generation node persists one (or more) of these media URLs to
|
|
464
|
+
* `jobs.output_data` on completion — `generate-image` → `imageUrl`,
|
|
465
|
+
* `generate-video` / `combine-videos` / `merge-video-audio` / `video-upscale`
|
|
466
|
+
* → `videoUrl` (+ `thumbnailUrl`), `text-to-speech` / `generate-music` →
|
|
467
|
+
* `audioUrl`. Resolved by {@link NodesResource.runAndWait}. Extra fields may be
|
|
468
|
+
* present, so the index signature is open.
|
|
469
|
+
*/
|
|
470
|
+
interface NodeJobOutput {
|
|
471
|
+
/** `text-to-speech` / `generate-music` / audio nodes write here. For
|
|
472
|
+
* `audio-separation` this is the primary stem (vocals). */
|
|
473
|
+
readonly audioUrl?: string;
|
|
474
|
+
/** `audio-separation` (Demucs) per-stem URLs. `vocalUrl`/`instrumentalUrl`
|
|
475
|
+
* in vocal/instrumental mode; the rest in full-stems mode. */
|
|
476
|
+
readonly vocalUrl?: string;
|
|
477
|
+
readonly instrumentalUrl?: string;
|
|
478
|
+
readonly drumsUrl?: string;
|
|
479
|
+
readonly bassUrl?: string;
|
|
480
|
+
readonly otherUrl?: string;
|
|
481
|
+
readonly guitarUrl?: string;
|
|
482
|
+
readonly pianoUrl?: string;
|
|
483
|
+
/** `generate-video` / `combine-videos` / `merge-video-audio` / `video-upscale` write here. */
|
|
484
|
+
readonly videoUrl?: string;
|
|
485
|
+
/** `generate-image` / `edit-image` / `extract-frame` write here. */
|
|
486
|
+
readonly imageUrl?: string;
|
|
487
|
+
/** Poster frame for video outputs. */
|
|
488
|
+
readonly thumbnailUrl?: string;
|
|
489
|
+
readonly [k: string]: unknown;
|
|
490
|
+
}
|
|
491
|
+
/** Options for {@link NodesResource.runAndWait} and {@link NodesResource.runMany}. */
|
|
492
|
+
interface RunAndWaitOptions {
|
|
493
|
+
/**
|
|
494
|
+
* Abort the run/poll loop. Aborting (or passing an already-aborted signal)
|
|
495
|
+
* stops polling and rejects with {@link JobAbortedError}.
|
|
496
|
+
*/
|
|
497
|
+
readonly signal?: AbortSignal;
|
|
498
|
+
/** Called with each lean status the poll loop observes (running → terminal). */
|
|
499
|
+
readonly onProgress?: (status: JobStatusResult) => void;
|
|
500
|
+
/** Poll interval in ms. Default 2000. */
|
|
501
|
+
readonly pollMs?: number;
|
|
502
|
+
/** Wall-clock cap before giving up, in ms. Default ~15 min (900_000). */
|
|
503
|
+
readonly maxMs?: number;
|
|
504
|
+
}
|
|
505
|
+
/** One settled result from {@link NodesResource.runMany}. */
|
|
506
|
+
interface RunManyResult {
|
|
507
|
+
readonly jobId: string;
|
|
508
|
+
readonly output: NodeJobOutput;
|
|
509
|
+
}
|
|
510
|
+
declare class NodesResource {
|
|
511
|
+
private client;
|
|
512
|
+
constructor(client: NodaroClient);
|
|
513
|
+
/** List all known node descriptors. Server caches publicly for 5 minutes. */
|
|
514
|
+
list(): Promise<{
|
|
515
|
+
data: NodeDescriptor[];
|
|
516
|
+
}>;
|
|
517
|
+
/** Get a single node descriptor by type slug (e.g. "generate-image"). */
|
|
518
|
+
get(type: string): Promise<{
|
|
519
|
+
data: NodeDescriptor;
|
|
520
|
+
}>;
|
|
521
|
+
/**
|
|
522
|
+
* Run a single node directly without wrapping it in a workflow. Posts
|
|
523
|
+
* `params` as the request body to `POST /v1/<type>` (the route convention
|
|
524
|
+
* every generation node follows: `generate-image`, `image-to-video`,
|
|
525
|
+
* `text-to-speech`, etc.).
|
|
526
|
+
*
|
|
527
|
+
* This is the SDK equivalent of the MCP server's verb tools — and the
|
|
528
|
+
* path the Nodaro CLI uses for `nodaro nodes run <type>`.
|
|
529
|
+
*
|
|
530
|
+
* Most node types are async: the response includes `{ jobId }` and the
|
|
531
|
+
* actual generation runs on a worker. Poll `client.jobs.get(jobId)` until
|
|
532
|
+
* completed. Inline node types (combine-text, etc.) return their full
|
|
533
|
+
* result synchronously without a `jobId` field.
|
|
534
|
+
*
|
|
535
|
+
* @param type Node type slug — must match an entry in the registry
|
|
536
|
+
* returned by `list()` (e.g. "generate-image").
|
|
537
|
+
* @param params Request body. Field names must match the node's
|
|
538
|
+
* `inputSchema` (see `get(type).inputSchema`).
|
|
539
|
+
*/
|
|
540
|
+
run(type: "generate-image", params?: GenerateImageParams): Promise<RunNodeResult>;
|
|
541
|
+
run(type: "generate-video", params?: GenerateVideoParams): Promise<RunNodeResult>;
|
|
542
|
+
run(type: "assemble-narrated-video", params?: AssembleNarratedVideoParams): Promise<RunNodeResult>;
|
|
543
|
+
run(type: string, params?: Record<string, unknown>): Promise<RunNodeResult>;
|
|
544
|
+
/**
|
|
545
|
+
* Run a single async node to completion: {@link run} it, extract the
|
|
546
|
+
* `{ jobId }`, then client-poll `jobs.getStatus(jobId)` every `pollMs`
|
|
547
|
+
* (default 2000) until a terminal status, up to `maxMs` (default ~15 min).
|
|
548
|
+
*
|
|
549
|
+
* Resolves the job's typed `output_data` ({@link NodeJobOutput}) on
|
|
550
|
+
* `completed`. Throws (all typed, catchable by `instanceof`):
|
|
551
|
+
* - {@link InsufficientCreditsError} / {@link StorageExceededError} etc. —
|
|
552
|
+
* surfaced by the underlying {@link run} on 402/413/… before any poll.
|
|
553
|
+
* - {@link JobFailedError} — terminal `failed`/`cancelled` (carries the
|
|
554
|
+
* job's `error_message` + `jobId`).
|
|
555
|
+
* - {@link JobTimeoutError} — `maxMs` deadline exceeded before terminal.
|
|
556
|
+
* - {@link JobAbortedError} — `signal` fired (or was already aborted);
|
|
557
|
+
* polling stops immediately.
|
|
558
|
+
*
|
|
559
|
+
* Polling is fully client-side (no server function blocks) — the same model
|
|
560
|
+
* thin clients use, lifted out of their hand-rolled run→poll loops.
|
|
561
|
+
*
|
|
562
|
+
* @param type Node type slug (e.g. "generate-video"). See {@link run}.
|
|
563
|
+
* @param params Request body — field names match the node's `inputSchema`.
|
|
564
|
+
* @param opts `signal` / `onProgress` / `pollMs` / `maxMs`.
|
|
565
|
+
*/
|
|
566
|
+
runAndWait(type: "generate-image", params?: GenerateImageParams, opts?: RunAndWaitOptions): Promise<NodeJobOutput>;
|
|
567
|
+
runAndWait(type: "generate-video", params?: GenerateVideoParams, opts?: RunAndWaitOptions): Promise<NodeJobOutput>;
|
|
568
|
+
runAndWait(type: "assemble-narrated-video", params?: AssembleNarratedVideoParams, opts?: RunAndWaitOptions): Promise<NodeJobOutput>;
|
|
569
|
+
runAndWait(type: string, params?: Record<string, unknown>, opts?: RunAndWaitOptions): Promise<NodeJobOutput>;
|
|
570
|
+
/**
|
|
571
|
+
* Fan out N async runs of the same node `type` to completion concurrently —
|
|
572
|
+
* the candidate-grid path (generate N stills/clips in parallel). Each runs
|
|
573
|
+
* via {@link runAndWait}; resolves once ALL settle, to an array of
|
|
574
|
+
* `{ jobId, output }` in input order. Rejects (and the rejection wins) if any
|
|
575
|
+
* single run rejects — same typed errors as {@link runAndWait}. A shared
|
|
576
|
+
* `signal` aborts the whole batch.
|
|
577
|
+
*
|
|
578
|
+
* @param type Node type slug, applied to every entry.
|
|
579
|
+
* @param paramsList One request body per candidate.
|
|
580
|
+
* @param opts Shared `signal` / `onProgress` / `pollMs` / `maxMs`.
|
|
581
|
+
*/
|
|
582
|
+
runMany(type: string, paramsList: Record<string, unknown>[], opts?: RunAndWaitOptions): Promise<RunManyResult[]>;
|
|
583
|
+
/** Poll an already-kicked job id until terminal; resolve output_data or throw. */
|
|
584
|
+
private pollJob;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* OAuth scopes that a developer app may request. Mirrors
|
|
589
|
+
* `backend/src/lib/scopes.ts#ALL_SCOPES`.
|
|
590
|
+
*/
|
|
591
|
+
type DeveloperAppScope = "workflows:read" | "workflows:write" | "workflows:execute" | "jobs:read" | "assets:read" | "assets:write" | "credits:read" | "apps:read" | "pipelines:read" | "pipelines:execute" | "pipelines:approve";
|
|
592
|
+
type DeveloperAppStatus = "active" | "suspended" | "pending_review";
|
|
593
|
+
interface DeveloperApp {
|
|
594
|
+
id: string;
|
|
595
|
+
name: string;
|
|
596
|
+
description: string | null;
|
|
597
|
+
logoUrl: string | null;
|
|
598
|
+
homepageUrl: string | null;
|
|
599
|
+
redirectUris: string[];
|
|
600
|
+
allowedOrigins: string[];
|
|
601
|
+
scopesRequested: DeveloperAppScope[];
|
|
602
|
+
clientId: string;
|
|
603
|
+
status: DeveloperAppStatus;
|
|
604
|
+
createdAt: string;
|
|
605
|
+
updatedAt: string;
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* One-shot create response — `clientSecret` is returned exactly ONCE here.
|
|
609
|
+
* Store it securely; subsequent `get`/`list` calls will not include it.
|
|
610
|
+
*/
|
|
611
|
+
interface CreateDeveloperAppResult extends DeveloperApp {
|
|
612
|
+
clientSecret: string;
|
|
613
|
+
}
|
|
614
|
+
interface CreateDeveloperAppInput {
|
|
615
|
+
name: string;
|
|
616
|
+
description?: string;
|
|
617
|
+
homepageUrl?: string;
|
|
618
|
+
logoUrl?: string;
|
|
619
|
+
/** At least 1, at most 10 redirect URIs. Each must be https or http://localhost. */
|
|
620
|
+
redirectUris: string[];
|
|
621
|
+
/** Up to 5 bare origins (no path/query/hash), e.g. "https://example.com". */
|
|
622
|
+
allowedOrigins?: string[];
|
|
623
|
+
/** At least 1 scope required. */
|
|
624
|
+
scopesRequested: DeveloperAppScope[];
|
|
625
|
+
}
|
|
626
|
+
interface UpdateDeveloperAppInput {
|
|
627
|
+
name?: string;
|
|
628
|
+
description?: string;
|
|
629
|
+
homepageUrl?: string;
|
|
630
|
+
logoUrl?: string;
|
|
631
|
+
redirectUris?: string[];
|
|
632
|
+
allowedOrigins?: string[];
|
|
633
|
+
scopesRequested?: DeveloperAppScope[];
|
|
634
|
+
}
|
|
635
|
+
interface RotateSecretResult {
|
|
636
|
+
/** New client secret. Returned exactly once — old secret is invalidated. */
|
|
637
|
+
clientSecret: string;
|
|
638
|
+
}
|
|
639
|
+
declare class DeveloperAppsResource {
|
|
640
|
+
private client;
|
|
641
|
+
constructor(client: NodaroClient);
|
|
642
|
+
/** List the authenticated user's developer apps. */
|
|
643
|
+
list(): Promise<{
|
|
644
|
+
data: DeveloperApp[];
|
|
645
|
+
}>;
|
|
646
|
+
/** Get a developer app by ID. */
|
|
647
|
+
get(id: string): Promise<{
|
|
648
|
+
data: DeveloperApp;
|
|
649
|
+
}>;
|
|
650
|
+
/**
|
|
651
|
+
* Create a new developer app. Returns the app PLUS a one-time `clientSecret`
|
|
652
|
+
* — store it now, the secret hash is the only copy kept server-side.
|
|
653
|
+
*/
|
|
654
|
+
create(input: CreateDeveloperAppInput): Promise<{
|
|
655
|
+
data: CreateDeveloperAppResult;
|
|
656
|
+
}>;
|
|
657
|
+
/** Update a developer app's metadata, redirect URIs, origins, or requested scopes. */
|
|
658
|
+
update(id: string, input: UpdateDeveloperAppInput): Promise<{
|
|
659
|
+
data: DeveloperApp;
|
|
660
|
+
}>;
|
|
661
|
+
/** Delete a developer app. Returns `{ success: true }`. */
|
|
662
|
+
delete(id: string): Promise<{
|
|
663
|
+
success: true;
|
|
664
|
+
}>;
|
|
665
|
+
/**
|
|
666
|
+
* Generate a new `clientSecret`. The previous secret is invalidated.
|
|
667
|
+
* Server returns ONLY the new secret, not the full app record.
|
|
668
|
+
*/
|
|
669
|
+
rotateSecret(id: string): Promise<RotateSecretResult>;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Server-side authorization-code exchange payload. Field names are snake_case
|
|
674
|
+
* per OAuth 2.0 (RFC 6749).
|
|
675
|
+
*/
|
|
676
|
+
interface ExchangeCodeInput {
|
|
677
|
+
client_id: string;
|
|
678
|
+
client_secret: string;
|
|
679
|
+
/** Authorization code received from the consent redirect. */
|
|
680
|
+
code: string;
|
|
681
|
+
/** Must match the redirect_uri used when issuing the code. */
|
|
682
|
+
redirect_uri: string;
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* `POST /v1/oauth/token` response — snake_case per RFC 6749. `expires_in` is
|
|
686
|
+
* the token's lifetime in seconds.
|
|
687
|
+
*/
|
|
688
|
+
interface AccessTokenResponse {
|
|
689
|
+
access_token: string;
|
|
690
|
+
token_type: "Bearer";
|
|
691
|
+
/** Space-separated list of granted scopes. */
|
|
692
|
+
scope: string;
|
|
693
|
+
expires_in: number;
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Public app metadata for consent screens. Only safe-to-display fields —
|
|
697
|
+
* no secret, no full origin list, no owner_user_id.
|
|
698
|
+
*/
|
|
699
|
+
interface OAuthAppInfo {
|
|
700
|
+
name: string;
|
|
701
|
+
description: string | null;
|
|
702
|
+
logoUrl: string | null;
|
|
703
|
+
homepageUrl: string | null;
|
|
704
|
+
scopesRequested: DeveloperAppScope[];
|
|
705
|
+
}
|
|
706
|
+
declare class OAuthResource {
|
|
707
|
+
private client;
|
|
708
|
+
constructor(client: NodaroClient);
|
|
709
|
+
/**
|
|
710
|
+
* Server-side authorization-code exchange. Sends the standard OAuth 2.0
|
|
711
|
+
* `application/json` body to `POST /v1/oauth/token`.
|
|
712
|
+
*
|
|
713
|
+
* NEVER call this from a browser — `client_secret` must stay on the server.
|
|
714
|
+
*/
|
|
715
|
+
exchangeCode(input: ExchangeCodeInput): Promise<AccessTokenResponse>;
|
|
716
|
+
/**
|
|
717
|
+
* Revoke an access token (RFC 7009). Always returns `{ success: true }`,
|
|
718
|
+
* even for unknown tokens — the spec forbids leaking token validity.
|
|
719
|
+
*/
|
|
720
|
+
revoke(token: string): Promise<{
|
|
721
|
+
success: true;
|
|
722
|
+
}>;
|
|
723
|
+
/**
|
|
724
|
+
* Get public app metadata for a consent screen.
|
|
725
|
+
* `GET /v1/oauth/app-info?client_id=<id>`. Public route — no auth needed.
|
|
726
|
+
*/
|
|
727
|
+
getAppInfo(clientId: string): Promise<OAuthAppInfo>;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* A published app — a workflow wrapped in a curated input/output presentation.
|
|
732
|
+
* Returned by the public `/v1/apps/browse` endpoint.
|
|
733
|
+
*/
|
|
734
|
+
interface PublishedApp {
|
|
735
|
+
id: string;
|
|
736
|
+
slug: string;
|
|
737
|
+
name: string;
|
|
738
|
+
description?: string | null;
|
|
739
|
+
creatorId: string;
|
|
740
|
+
creatorName?: string | null;
|
|
741
|
+
thumbnailUrl?: string | null;
|
|
742
|
+
category?: string | null;
|
|
743
|
+
isFeatured?: boolean;
|
|
744
|
+
runCount?: number;
|
|
745
|
+
createdAt: string;
|
|
746
|
+
updatedAt: string;
|
|
747
|
+
}
|
|
748
|
+
interface ListAppsParams {
|
|
749
|
+
/** Substring search across app name + description. */
|
|
750
|
+
search?: string;
|
|
751
|
+
/** Page size; backend caps at 50. */
|
|
752
|
+
limit?: number;
|
|
753
|
+
/** Cursor token returned by the previous page. */
|
|
754
|
+
cursor?: string;
|
|
755
|
+
/** Filter to a single category slug. */
|
|
756
|
+
category?: string;
|
|
757
|
+
}
|
|
758
|
+
interface ListAppsResult {
|
|
759
|
+
data: PublishedApp[];
|
|
760
|
+
nextCursor?: string | null;
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* App detail — includes the input schema (required + optional fields end users
|
|
764
|
+
* fill in) and the output mapping (which workflow nodes produce which display
|
|
765
|
+
* cards).
|
|
766
|
+
*/
|
|
767
|
+
interface PublishedAppDetail extends PublishedApp {
|
|
768
|
+
inputSchema: Record<string, unknown>;
|
|
769
|
+
outputs: Array<{
|
|
770
|
+
nodeId: string;
|
|
771
|
+
label: string;
|
|
772
|
+
type: string;
|
|
773
|
+
}>;
|
|
774
|
+
}
|
|
775
|
+
interface AppRunResult {
|
|
776
|
+
/** The execution-id that was started — poll via client.executions.get(). */
|
|
777
|
+
executionId: string;
|
|
778
|
+
status: "pending" | "running";
|
|
779
|
+
/** App-run id (distinct from executionId — used by listRuns/getRun). */
|
|
780
|
+
runId?: string;
|
|
781
|
+
}
|
|
782
|
+
interface AppRun {
|
|
783
|
+
id: string;
|
|
784
|
+
appSlug: string;
|
|
785
|
+
executionId: string;
|
|
786
|
+
status: "pending" | "running" | "completed" | "failed" | "cancelled";
|
|
787
|
+
inputs: Record<string, unknown>;
|
|
788
|
+
outputs?: Array<{
|
|
789
|
+
nodeId: string;
|
|
790
|
+
type: string;
|
|
791
|
+
url?: string;
|
|
792
|
+
text?: string;
|
|
793
|
+
}>;
|
|
794
|
+
startedAt: string;
|
|
795
|
+
finishedAt?: string | null;
|
|
796
|
+
}
|
|
797
|
+
interface ListAppRunsParams {
|
|
798
|
+
limit?: number;
|
|
799
|
+
cursor?: string;
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
* Result of a soft-delete (archive) operation. The run is moved to the user's
|
|
803
|
+
* archive in the Nodaro UI; restoration and permanent deletion are UI-only by
|
|
804
|
+
* design — SDK / MCP / API delete callers can't accidentally destroy data.
|
|
805
|
+
*/
|
|
806
|
+
interface DeleteAppRunResult {
|
|
807
|
+
success: true;
|
|
808
|
+
archived: true;
|
|
809
|
+
}
|
|
810
|
+
declare class AppsResource {
|
|
811
|
+
private client;
|
|
812
|
+
constructor(client: NodaroClient);
|
|
813
|
+
/** List published apps. Public — no auth required for community apps. */
|
|
814
|
+
list(params?: ListAppsParams): Promise<ListAppsResult>;
|
|
815
|
+
/** Get one app's metadata + input schema by slug. */
|
|
816
|
+
get(slug: string): Promise<{
|
|
817
|
+
data: PublishedAppDetail;
|
|
818
|
+
}>;
|
|
819
|
+
/**
|
|
820
|
+
* Trigger an app run with the given input values. The keys in `inputs` must
|
|
821
|
+
* match the app's input-schema field names (see `get(slug).inputSchema`).
|
|
822
|
+
* Returns the execution-id for status polling via client.executions.get().
|
|
823
|
+
*/
|
|
824
|
+
run(slug: string, inputs?: Record<string, unknown>): Promise<AppRunResult>;
|
|
825
|
+
/** List past runs for an app (the caller must own the app or the runs). */
|
|
826
|
+
listRuns(slug: string, params?: ListAppRunsParams): Promise<{
|
|
827
|
+
data: AppRun[];
|
|
828
|
+
nextCursor?: string | null;
|
|
829
|
+
}>;
|
|
830
|
+
/** Get one app-run by id. */
|
|
831
|
+
getRun(slug: string, runId: string): Promise<{
|
|
832
|
+
data: AppRun;
|
|
833
|
+
}>;
|
|
834
|
+
/**
|
|
835
|
+
* Archive (soft-delete) a published-app run. The run is hidden from the
|
|
836
|
+
* default run list and can be restored or permanently deleted from the
|
|
837
|
+
* archive view at https://app.nodaro.ai/archived-runs.
|
|
838
|
+
*
|
|
839
|
+
* @param slug The published app's slug (the last path segment of its URL).
|
|
840
|
+
* @param runId The run's UUID.
|
|
841
|
+
*/
|
|
842
|
+
deleteRun(slug: string, runId: string): Promise<DeleteAppRunResult>;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/**
|
|
846
|
+
* Re-export the shared `EntityStyle` union (realistic | anime | 3d-pixar |
|
|
847
|
+
* illustration) and `CHARACTER_STYLES` runtime tuple so SDK consumers don't
|
|
848
|
+
* have to add `@nodaro/shared` as a second dependency just to typecheck the
|
|
849
|
+
* `style` field. Single source of truth lives in `@nodaro/shared/entity-prompts`.
|
|
850
|
+
*/
|
|
851
|
+
/**
|
|
852
|
+
* Re-export the 4-value aspect-ratio union accepted by the generate-character*
|
|
853
|
+
* routes. Single source of truth lives in `@nodaro/shared`. See
|
|
854
|
+
* `CHARACTER_ASPECT_DEFAULTS` for the per-asset-type defaults.
|
|
855
|
+
*/
|
|
856
|
+
/**
|
|
857
|
+
* Structured Person composer, re-exported from `@nodaro/shared` so SDK
|
|
858
|
+
* consumers can build a detailed person description — Identity, Body, and the
|
|
859
|
+
* Face facial-geometry layer (cheekbones, canthal tilt, eyelid type, lip
|
|
860
|
+
* fullness/shape, nose tip, etc.) — and feed it as `seedPrompt` / `description`
|
|
861
|
+
* to `generate()` / `upsert()` without adding `@nodaro/shared` as a second dep.
|
|
862
|
+
*
|
|
863
|
+
* `buildPersonSeedPrompt` collapses a `PersonValue` into the same comma-joined
|
|
864
|
+
* fragment the editor's Person picker produces (e.g. "almond-shaped eyes,
|
|
865
|
+
* sharply sculpted high cheekbones, full plump lips"). Returns "" when empty.
|
|
866
|
+
*/
|
|
867
|
+
/**
|
|
868
|
+
* A character record returned by Nodaro's REST API. Mirrors the camelCase
|
|
869
|
+
* shape produced by `backend/src/routes/characters.ts::toCamel()`.
|
|
870
|
+
*
|
|
871
|
+
* `expressions`, `poses`, `motions`, `angles`, `bodyAngles`,
|
|
872
|
+
* `lightingVariations` are independent buckets keyed by a human-readable
|
|
873
|
+
* variant name (e.g. `"smile"`, `"standing"`, `"3/4 left"`). Each entry's
|
|
874
|
+
* `url` points at an R2-hosted asset.
|
|
875
|
+
*
|
|
876
|
+
* Identity-foundation fields:
|
|
877
|
+
* - `referencePhotos` — caller-supplied real-life refs (max one per
|
|
878
|
+
* non-`other` kind; cap 20 total). Drive the i2v / i2i path when a
|
|
879
|
+
* provider supports multi-image conditioning.
|
|
880
|
+
* - `realLifeRefsByVariant` — per-variant reference URLs (cap 20 keys,
|
|
881
|
+
* 5 URLs per key). Keys are lowercased+trimmed.
|
|
882
|
+
* - `referenceVideosByVariant` — per-label user-uploaded reference VIDEO
|
|
883
|
+
* URLs (cap 20 keys, 5 URLs per key, lowercased+trimmed keys). Mirrors
|
|
884
|
+
* `realLifeRefsByVariant` for video clips (e.g. emotion takes). Read the
|
|
885
|
+
* chosen URLs off the row to drive generate-video's `referenceVideoUrls`.
|
|
886
|
+
* - `seedPrompt` — short prompt fragment that scaffolds portrait gen.
|
|
887
|
+
* - `canonicalDescription` — ~80–120-word LLM-authored visual caption,
|
|
888
|
+
* populated by `approvePortrait()` / `recaption()`.
|
|
889
|
+
*/
|
|
890
|
+
interface Character {
|
|
891
|
+
id: string;
|
|
892
|
+
userId: string;
|
|
893
|
+
/** Canvas node linkage. `null` only on legacy clone rows that predate the
|
|
894
|
+
* clone-side node_id fix (new clones mint one; creates always had one). */
|
|
895
|
+
nodeId: string | null;
|
|
896
|
+
projectId: string | null;
|
|
897
|
+
name: string;
|
|
898
|
+
description: string | null;
|
|
899
|
+
gender: string | null;
|
|
900
|
+
style: string | null;
|
|
901
|
+
baseOutfit: string | null;
|
|
902
|
+
sourceImageUrl: string | null;
|
|
903
|
+
/** MODEL_CATALOG image-model id the main image was generated with (or `null`).
|
|
904
|
+
* Set on create (the provider you generated with) + editable via `upsert`. */
|
|
905
|
+
imageProvider: string | null;
|
|
906
|
+
expressions: Array<{
|
|
907
|
+
name: string;
|
|
908
|
+
url: string;
|
|
909
|
+
}> | null;
|
|
910
|
+
poses: Array<{
|
|
911
|
+
name: string;
|
|
912
|
+
url: string;
|
|
913
|
+
}> | null;
|
|
914
|
+
lightingVariations: Array<{
|
|
915
|
+
name: string;
|
|
916
|
+
url: string;
|
|
917
|
+
}> | null;
|
|
918
|
+
angles: Array<{
|
|
919
|
+
name: string;
|
|
920
|
+
url: string;
|
|
921
|
+
}> | null;
|
|
922
|
+
bodyAngles: Array<{
|
|
923
|
+
name: string;
|
|
924
|
+
url: string;
|
|
925
|
+
}> | null;
|
|
926
|
+
motions: Array<{
|
|
927
|
+
name: string;
|
|
928
|
+
url: string;
|
|
929
|
+
}> | null;
|
|
930
|
+
/** Named Character Boards — dense reference sheets, one per persona/look
|
|
931
|
+
* (the `generate-image/character-board` factory preset rendered from the
|
|
932
|
+
* character's images). A first-class bucket: community publish snapshots it
|
|
933
|
+
* and clone hands the consumer their own copy to extend. Defaults to `[]`.
|
|
934
|
+
* `type` marks an image-collage `"identity"` sheet vs a plain `"looks"`
|
|
935
|
+
* board; `sourceImages` are the R2 URLs it was collaged from. Both optional
|
|
936
|
+
* + backward-compatible (legacy boards have neither). */
|
|
937
|
+
boards?: Array<{
|
|
938
|
+
name: string;
|
|
939
|
+
url: string;
|
|
940
|
+
type?: "looks" | "identity";
|
|
941
|
+
sourceImages?: string[];
|
|
942
|
+
}> | null;
|
|
943
|
+
/** Per-label user-uploaded reference VIDEO URLs (R2), keyed by a
|
|
944
|
+
* caller-owned label (lowercased+trimmed server-side). Mirrors
|
|
945
|
+
* `realLifeRefsByVariant` for video clips; read the chosen URLs off the row
|
|
946
|
+
* to feed generate-video's `referenceVideoUrls`. Defaults to `{}`. */
|
|
947
|
+
referenceVideosByVariant?: Record<string, string[]> | null;
|
|
948
|
+
/** The user's chosen DEFAULT asset take per variant (Studio version history).
|
|
949
|
+
* OPAQUE map: key `"<bucket>:<variant>"` (e.g. `"bodyAngles:front"`,
|
|
950
|
+
* `"expressions:smile"`) → the chosen asset URL (one already present in that
|
|
951
|
+
* bucket). Stored verbatim — keys are NOT normalized; soft-capped server-side
|
|
952
|
+
* at 200 keys / 2048-char values (overflow dropped silently). Defaults to `{}`. */
|
|
953
|
+
selectedAssetByVariant?: Record<string, string> | null;
|
|
954
|
+
/** `voiceType` records the selected voice's KIND (premade voices are
|
|
955
|
+
* addressed by name; library/custom voices by id at text-to-speech time).
|
|
956
|
+
* `previewUrl` is a playable audio sample (the voice's `preview_url` / clone
|
|
957
|
+
* sample) the studio plays in an `<audio>` element — persisted so Voice Library
|
|
958
|
+
* voices (which have no by-id lookup) stay previewable after reload. Client-
|
|
959
|
+
* played only; the server never fetches it. `ttsProvider` is the library
|
|
960
|
+
* voice's verified TTS provider (see `SharedVoice.recommendedProvider`) —
|
|
961
|
+
* send it as the text-to-speech `provider` so the voice renders on a model
|
|
962
|
+
* it's verified for. All optional — a character may have no voice, or a
|
|
963
|
+
* legacy voice predating these fields. */
|
|
964
|
+
voice: {
|
|
965
|
+
voiceId: string;
|
|
966
|
+
voiceName: string;
|
|
967
|
+
traits: string;
|
|
968
|
+
voiceType?: "premade" | "library" | "custom";
|
|
969
|
+
previewUrl?: string;
|
|
970
|
+
ttsProvider?: TtsProvider;
|
|
971
|
+
} | null;
|
|
972
|
+
personality: {
|
|
973
|
+
mood: string;
|
|
974
|
+
speechStyle: string;
|
|
975
|
+
movementStyle: string;
|
|
976
|
+
behavioralNotes: string;
|
|
977
|
+
} | null;
|
|
978
|
+
/** ~80–120-word LLM-authored visual caption (approve-portrait / recaption).
|
|
979
|
+
* Optional on the read surface so existing literal consumers don't break;
|
|
980
|
+
* the route always returns it (string | null). */
|
|
981
|
+
canonicalDescription?: string | null;
|
|
982
|
+
/** Identity-lock strength for Character Studio asset generation. */
|
|
983
|
+
identityLock?: "off" | "soft" | "strict";
|
|
984
|
+
deletedAt: string | null;
|
|
985
|
+
createdAt: string;
|
|
986
|
+
updatedAt: string;
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* GET /v1/characters/:id appends three live-progress buckets the studio uses
|
|
990
|
+
* to rehydrate spinners after a reload. Optional in the SDK surface — they
|
|
991
|
+
* don't appear on `list()` rows.
|
|
992
|
+
*/
|
|
993
|
+
interface CharacterDetail extends Character {
|
|
994
|
+
pendingJobs?: Array<{
|
|
995
|
+
jobId: string;
|
|
996
|
+
assetType: "expressions" | "poses" | "angles" | "bodyAngles" | "lighting" | "motions";
|
|
997
|
+
name: string;
|
|
998
|
+
}>;
|
|
999
|
+
portraitCandidates?: Array<{
|
|
1000
|
+
jobId: string;
|
|
1001
|
+
url: string | undefined;
|
|
1002
|
+
progress: number;
|
|
1003
|
+
status: string;
|
|
1004
|
+
}>;
|
|
1005
|
+
previousCandidates?: Array<{
|
|
1006
|
+
jobId: string;
|
|
1007
|
+
url: string;
|
|
1008
|
+
createdAt: string;
|
|
1009
|
+
}>;
|
|
1010
|
+
}
|
|
1011
|
+
type ReferencePhotoKind = "frontFace" | "sideLeft" | "sideRight" | "threeQuarterLeft" | "threeQuarterRight" | "frontBody" | "other";
|
|
1012
|
+
interface ReferencePhoto {
|
|
1013
|
+
url: string;
|
|
1014
|
+
kind: ReferencePhotoKind;
|
|
1015
|
+
}
|
|
1016
|
+
/**
|
|
1017
|
+
* Body for `client.characters.upsert()`. Mirrors `upsertCharacterBody` in
|
|
1018
|
+
* `backend/src/routes/characters.ts`. Omitting `id` triggers an INSERT;
|
|
1019
|
+
* supplying it triggers an UPDATE that only writes the fields you pass —
|
|
1020
|
+
* undefined keys are NOT touched on the row.
|
|
1021
|
+
*
|
|
1022
|
+
* `name` is optional at the type level. The route requires `name` on INSERT
|
|
1023
|
+
* (id absent) and rejects with `validation_error` otherwise; on UPDATE the
|
|
1024
|
+
* route just ignores `name` when omitted, which lets partial updates like
|
|
1025
|
+
* `update(id, { gender: "female" })` succeed without re-sending the same
|
|
1026
|
+
* name the caller already has.
|
|
1027
|
+
*/
|
|
1028
|
+
interface UpsertCharacterInput {
|
|
1029
|
+
/** UUID of the character row; omit to create. */
|
|
1030
|
+
id?: string;
|
|
1031
|
+
/** Canvas node id the character belongs to. REQUIRED on create (the route
|
|
1032
|
+
* 400s without it, like `name`); optional on update — the update branch
|
|
1033
|
+
* never touches node_id, so partial updates needn't round-trip it. */
|
|
1034
|
+
nodeId?: string;
|
|
1035
|
+
workflowId?: string;
|
|
1036
|
+
projectId?: string;
|
|
1037
|
+
name?: string;
|
|
1038
|
+
description?: string;
|
|
1039
|
+
gender?: string;
|
|
1040
|
+
style?: string;
|
|
1041
|
+
baseOutfit?: string;
|
|
1042
|
+
sourceImageUrl?: string;
|
|
1043
|
+
/** Persistent image-model id (a MODEL_CATALOG image model). Validated
|
|
1044
|
+
* server-side — unknown / non-image / "" is stored as `null`. */
|
|
1045
|
+
imageProvider?: string | null;
|
|
1046
|
+
expressions?: Array<{
|
|
1047
|
+
name: string;
|
|
1048
|
+
url: string;
|
|
1049
|
+
}>;
|
|
1050
|
+
poses?: Array<{
|
|
1051
|
+
name: string;
|
|
1052
|
+
url: string;
|
|
1053
|
+
}>;
|
|
1054
|
+
lightingVariations?: Array<{
|
|
1055
|
+
name: string;
|
|
1056
|
+
url: string;
|
|
1057
|
+
}>;
|
|
1058
|
+
angles?: Array<{
|
|
1059
|
+
name: string;
|
|
1060
|
+
url: string;
|
|
1061
|
+
}>;
|
|
1062
|
+
bodyAngles?: Array<{
|
|
1063
|
+
name: string;
|
|
1064
|
+
url: string;
|
|
1065
|
+
}>;
|
|
1066
|
+
motions?: Array<{
|
|
1067
|
+
name: string;
|
|
1068
|
+
url: string;
|
|
1069
|
+
}>;
|
|
1070
|
+
/** Named Character Boards (see `Character.boards`) — whole-array replace,
|
|
1071
|
+
* like the asset buckets. Server caps: 24 boards, 200-char names, 30
|
|
1072
|
+
* sourceImages per board. */
|
|
1073
|
+
boards?: Array<{
|
|
1074
|
+
name: string;
|
|
1075
|
+
url: string;
|
|
1076
|
+
type?: "looks" | "identity";
|
|
1077
|
+
sourceImages?: string[];
|
|
1078
|
+
}>;
|
|
1079
|
+
/** See `Character.voice` — persisted alongside the voice so TTS can resolve a
|
|
1080
|
+
* library/custom voice by id, `previewUrl` keeps the sample playable after
|
|
1081
|
+
* reload, and `ttsProvider` keeps generation on a model the voice is
|
|
1082
|
+
* verified for. All optional. */
|
|
1083
|
+
voice?: {
|
|
1084
|
+
voiceId: string;
|
|
1085
|
+
voiceName: string;
|
|
1086
|
+
traits: string;
|
|
1087
|
+
voiceType?: "premade" | "library" | "custom";
|
|
1088
|
+
previewUrl?: string;
|
|
1089
|
+
ttsProvider?: TtsProvider;
|
|
1090
|
+
} | null;
|
|
1091
|
+
personality?: {
|
|
1092
|
+
mood: string;
|
|
1093
|
+
speechStyle: string;
|
|
1094
|
+
movementStyle: string;
|
|
1095
|
+
behavioralNotes: string;
|
|
1096
|
+
} | null;
|
|
1097
|
+
seedPrompt?: string;
|
|
1098
|
+
canonicalDescription?: string;
|
|
1099
|
+
/** Identity-lock strength for Character Studio asset generation (off/soft/strict). */
|
|
1100
|
+
identityLock?: "off" | "soft" | "strict";
|
|
1101
|
+
referencePhotos?: ReferencePhoto[];
|
|
1102
|
+
/** Per-variant real-life reference URLs. Keys are lowercased+trimmed server-side. */
|
|
1103
|
+
realLifeRefsByVariant?: Record<string, string[]>;
|
|
1104
|
+
/** Per-label user-uploaded reference VIDEO URLs (e.g. emotion takes). Keys
|
|
1105
|
+
* are lowercased+trimmed server-side; max 20 keys, 5 URLs each. Stored R2
|
|
1106
|
+
* URLs are read back off the row to drive generate-video's
|
|
1107
|
+
* `referenceVideoUrls`. */
|
|
1108
|
+
referenceVideosByVariant?: Record<string, string[]>;
|
|
1109
|
+
/** The user's chosen DEFAULT asset take per variant. OPAQUE
|
|
1110
|
+
* `"<bucket>:<variant>"` → chosen-URL map; a write REPLACES the whole map
|
|
1111
|
+
* (the studio sends the full map each save). Omit to leave the row untouched.
|
|
1112
|
+
* Keys stored verbatim; soft-capped server-side at 200 keys / 2048-char values. */
|
|
1113
|
+
selectedAssetByVariant?: Record<string, string>;
|
|
1114
|
+
}
|
|
1115
|
+
interface UpsertCharacterResult {
|
|
1116
|
+
id: string;
|
|
1117
|
+
name?: string;
|
|
1118
|
+
}
|
|
1119
|
+
interface ListCharactersParams {
|
|
1120
|
+
/** Restrict to a single project. */
|
|
1121
|
+
projectId?: string;
|
|
1122
|
+
/** When true, return archived characters instead of active ones. */
|
|
1123
|
+
archived?: boolean;
|
|
1124
|
+
/**
|
|
1125
|
+
* Max rows to return. Server defaults to 100, caps at 500. Omit to take the
|
|
1126
|
+
* server default — passing it just narrows further.
|
|
1127
|
+
*/
|
|
1128
|
+
limit?: number;
|
|
1129
|
+
}
|
|
1130
|
+
interface DuplicateCharacterInput {
|
|
1131
|
+
/** Optional canvas node id to bind the new row to. */
|
|
1132
|
+
nodeId?: string;
|
|
1133
|
+
/** Optional project to drop the new row into. */
|
|
1134
|
+
projectId?: string;
|
|
1135
|
+
}
|
|
1136
|
+
interface CharacterUsage {
|
|
1137
|
+
workflowCount: number;
|
|
1138
|
+
workflows: Array<{
|
|
1139
|
+
id: string;
|
|
1140
|
+
name: string;
|
|
1141
|
+
}>;
|
|
1142
|
+
}
|
|
1143
|
+
/**
|
|
1144
|
+
* Input for `client.characters.generate()` — fires the
|
|
1145
|
+
* `POST /v1/generate-character` route. Produces 1–10 portrait candidates;
|
|
1146
|
+
* each lands as one `jobs` row in `pending` state and is then enqueued for
|
|
1147
|
+
* the worker.
|
|
1148
|
+
*
|
|
1149
|
+
* Provide at least one of `seedPrompt`, `referencePhotos`, or `description`
|
|
1150
|
+
* (the backend's refinement rejects empty input with `validation_error`).
|
|
1151
|
+
*
|
|
1152
|
+
* When `attachToCharacterId` is set, the worker writes the resulting URL
|
|
1153
|
+
* directly to `characters.source_image_url` on completion — caller doesn't
|
|
1154
|
+
* need a separate `approvePortrait` call for single-candidate runs.
|
|
1155
|
+
*/
|
|
1156
|
+
interface GenerateCharacterInput {
|
|
1157
|
+
name: string;
|
|
1158
|
+
description?: string;
|
|
1159
|
+
userPrompt?: string;
|
|
1160
|
+
gender?: string;
|
|
1161
|
+
style?: EntityStyle;
|
|
1162
|
+
baseOutfit?: string;
|
|
1163
|
+
sourceImageUrl?: string;
|
|
1164
|
+
provider?: string;
|
|
1165
|
+
/** Auto-attach the result to this character row. */
|
|
1166
|
+
attachToCharacterId?: string;
|
|
1167
|
+
seedPrompt?: string;
|
|
1168
|
+
referencePhotos?: ReferencePhoto[];
|
|
1169
|
+
/** Number of candidate images to generate (1–10; server-validated). */
|
|
1170
|
+
count?: number;
|
|
1171
|
+
/**
|
|
1172
|
+
* Explicit aspect ratio. Highest precedence — overrides both the character
|
|
1173
|
+
* node toggle and the per-asset-type default (portraits default to `3:4`).
|
|
1174
|
+
* Must be one of the 4-value `CharacterAspectRatio` union.
|
|
1175
|
+
*/
|
|
1176
|
+
aspectRatio?: CharacterAspectRatio;
|
|
1177
|
+
/**
|
|
1178
|
+
* Character node toggle (per-canvas-node `defaultAssetAspectRatio`). Wins
|
|
1179
|
+
* against the per-asset-type default, loses to `aspectRatio`.
|
|
1180
|
+
*/
|
|
1181
|
+
characterNodeAspectRatio?: CharacterAspectRatio;
|
|
1182
|
+
/**
|
|
1183
|
+
* Credit-affecting quality tier (e.g. `"high"` for gpt-image). Priced like
|
|
1184
|
+
* generate-image (composite ids such as `gpt-image:high`); values the chosen
|
|
1185
|
+
* model doesn't support are ignored server-side, never rejected.
|
|
1186
|
+
*/
|
|
1187
|
+
quality?: string;
|
|
1188
|
+
/**
|
|
1189
|
+
* Credit-affecting output resolution (e.g. `"2K"` / `"4K"` / `"2 MP"`).
|
|
1190
|
+
* Priced like generate-image (composite ids such as `nano-banana-pro:4K`);
|
|
1191
|
+
* values the chosen model doesn't support are ignored server-side, never
|
|
1192
|
+
* rejected.
|
|
1193
|
+
*/
|
|
1194
|
+
resolution?: string;
|
|
1195
|
+
}
|
|
1196
|
+
interface GenerateCharacterResult {
|
|
1197
|
+
/** First job-id; convenience alias for `jobIds[0]`. */
|
|
1198
|
+
jobId: string;
|
|
1199
|
+
/** All job-ids when `count > 1`. */
|
|
1200
|
+
jobIds: string[];
|
|
1201
|
+
}
|
|
1202
|
+
interface GenerateAssetInput {
|
|
1203
|
+
assetType: "expressions" | "poses" | "lighting" | "angles" | "headAngles" | "bodyAngles" | "custom";
|
|
1204
|
+
/** The named variant (e.g. `"smile"`, `"standing"`, `"3/4 left"`). */
|
|
1205
|
+
variant: string;
|
|
1206
|
+
/** Display name of the character; appears in the prompt. */
|
|
1207
|
+
name: string;
|
|
1208
|
+
description?: string;
|
|
1209
|
+
userPrompt?: string;
|
|
1210
|
+
gender?: string;
|
|
1211
|
+
style?: EntityStyle;
|
|
1212
|
+
baseOutfit?: string;
|
|
1213
|
+
sourceImageUrl?: string;
|
|
1214
|
+
/** Real-life reference URLs (cap 5). */
|
|
1215
|
+
realLifeRefs?: string[];
|
|
1216
|
+
provider?: string;
|
|
1217
|
+
/** Auto-attach to character row + asset bucket on completion. */
|
|
1218
|
+
attachToCharacterId?: string;
|
|
1219
|
+
/** Shared type — auto-includes new buckets (sheets/detail_closeups/outfit_variations); mirrors objects.ts/locations.ts. */
|
|
1220
|
+
attachToColumn?: CharacterAttachColumn;
|
|
1221
|
+
attachName?: string;
|
|
1222
|
+
/**
|
|
1223
|
+
* Explicit aspect ratio. Highest precedence — overrides both the character
|
|
1224
|
+
* node toggle and the per-asset-type default (expressions=1:1, poses=9:16,
|
|
1225
|
+
* headAngles=3:4, bodyAngles=9:16, lighting=3:4, angles=3:4, custom=3:4).
|
|
1226
|
+
*/
|
|
1227
|
+
aspectRatio?: CharacterAspectRatio;
|
|
1228
|
+
/**
|
|
1229
|
+
* Character node toggle (per-canvas-node `defaultAssetAspectRatio`). Wins
|
|
1230
|
+
* against the per-asset-type default, loses to `aspectRatio`.
|
|
1231
|
+
*/
|
|
1232
|
+
characterNodeAspectRatio?: CharacterAspectRatio;
|
|
1233
|
+
/**
|
|
1234
|
+
* Credit-affecting quality tier (e.g. `"high"` for gpt-image). Values the
|
|
1235
|
+
* chosen model doesn't support are ignored server-side, never rejected.
|
|
1236
|
+
*/
|
|
1237
|
+
quality?: string;
|
|
1238
|
+
/**
|
|
1239
|
+
* Credit-affecting output resolution (e.g. `"2K"` / `"4K"` / `"2 MP"`).
|
|
1240
|
+
* Values the chosen model doesn't support are ignored server-side, never
|
|
1241
|
+
* rejected.
|
|
1242
|
+
*/
|
|
1243
|
+
resolution?: string;
|
|
1244
|
+
}
|
|
1245
|
+
interface GenerateMotionInput {
|
|
1246
|
+
motionPrompt: string;
|
|
1247
|
+
/** Optional when `attachToCharacterId` is set — falls back to the row's portrait. */
|
|
1248
|
+
sourceImageUrl?: string;
|
|
1249
|
+
provider?: string;
|
|
1250
|
+
name: string;
|
|
1251
|
+
description?: string;
|
|
1252
|
+
motionDescription?: string;
|
|
1253
|
+
gender?: string;
|
|
1254
|
+
style?: EntityStyle;
|
|
1255
|
+
baseOutfit?: string;
|
|
1256
|
+
realLifeRefs?: string[];
|
|
1257
|
+
attachToCharacterId?: string;
|
|
1258
|
+
attachName?: string;
|
|
1259
|
+
/**
|
|
1260
|
+
* Explicit aspect ratio. Highest precedence — overrides both the character
|
|
1261
|
+
* node toggle and the motions default (`9:16`).
|
|
1262
|
+
*/
|
|
1263
|
+
aspectRatio?: CharacterAspectRatio;
|
|
1264
|
+
/**
|
|
1265
|
+
* Character node toggle (per-canvas-node `defaultAssetAspectRatio`). Wins
|
|
1266
|
+
* against the motions default, loses to `aspectRatio`.
|
|
1267
|
+
*/
|
|
1268
|
+
characterNodeAspectRatio?: CharacterAspectRatio;
|
|
1269
|
+
}
|
|
1270
|
+
interface ApprovePortraitResult {
|
|
1271
|
+
portraitUrl: string;
|
|
1272
|
+
/**
|
|
1273
|
+
* LLM-authored caption. `null` when the LLM call failed during the approval
|
|
1274
|
+
* — the portrait is still set; call `recaption()` to retry.
|
|
1275
|
+
*/
|
|
1276
|
+
canonicalDescription: string | null;
|
|
1277
|
+
}
|
|
1278
|
+
interface RecaptionResult {
|
|
1279
|
+
canonicalDescription: string;
|
|
1280
|
+
}
|
|
1281
|
+
declare class CharactersResource {
|
|
1282
|
+
private client;
|
|
1283
|
+
constructor(client: NodaroClient);
|
|
1284
|
+
/**
|
|
1285
|
+
* List the caller's characters. By default returns active characters only;
|
|
1286
|
+
* pass `archived: true` to fetch soft-deleted rows for an "archive" view.
|
|
1287
|
+
* When `projectId` is set, only characters belonging to that project are
|
|
1288
|
+
* returned.
|
|
1289
|
+
*/
|
|
1290
|
+
list(params?: ListCharactersParams): Promise<{
|
|
1291
|
+
characters: Character[];
|
|
1292
|
+
}>;
|
|
1293
|
+
/**
|
|
1294
|
+
* Fetch a single character including in-flight portrait / asset job state.
|
|
1295
|
+
* Soft-deleted (archived) rows are returned by id intentionally so canvas
|
|
1296
|
+
* nodes that hold a stale `characterDbId` keep loading.
|
|
1297
|
+
*/
|
|
1298
|
+
get(id: string): Promise<CharacterDetail>;
|
|
1299
|
+
/**
|
|
1300
|
+
* Create or update a character. Omit `id` to create; supply it to update
|
|
1301
|
+
* (only the fields you pass get written — undefined keys are untouched).
|
|
1302
|
+
*
|
|
1303
|
+
* If the caller-supplied `name` collides with an existing active character
|
|
1304
|
+
* for this user, the request returns 409 `name_taken`. To auto-number a
|
|
1305
|
+
* placeholder, pass the placeholder name from `@nodaro/shared` and the
|
|
1306
|
+
* server will derive "Untitled character 2", "Untitled character 3", etc.
|
|
1307
|
+
*/
|
|
1308
|
+
upsert(input: UpsertCharacterInput): Promise<UpsertCharacterResult>;
|
|
1309
|
+
/**
|
|
1310
|
+
* Convenience wrapper around `upsert()` for creating new characters.
|
|
1311
|
+
* Equivalent to `upsert({ ...input, id: undefined })`. `name` is REQUIRED
|
|
1312
|
+
* on create — the route 400s on INSERT-without-name; we narrow the type
|
|
1313
|
+
* here so callers fail at compile-time rather than runtime.
|
|
1314
|
+
*/
|
|
1315
|
+
create(input: Omit<UpsertCharacterInput, "id"> & {
|
|
1316
|
+
name: string;
|
|
1317
|
+
}): Promise<UpsertCharacterResult>;
|
|
1318
|
+
/**
|
|
1319
|
+
* Convenience wrapper around `upsert()` for updating an existing character.
|
|
1320
|
+
* Equivalent to `upsert({ ...input, id })`.
|
|
1321
|
+
*/
|
|
1322
|
+
update(id: string, input: Omit<UpsertCharacterInput, "id">): Promise<UpsertCharacterResult>;
|
|
1323
|
+
/**
|
|
1324
|
+
* Soft-delete (archive) a character. The row is hidden from `list()` by
|
|
1325
|
+
* default but still loadable via `get(id)` so canvas nodes pointing at it
|
|
1326
|
+
* keep working. Restore with `restore(id)`.
|
|
1327
|
+
*/
|
|
1328
|
+
delete(id: string): Promise<{
|
|
1329
|
+
success: true;
|
|
1330
|
+
archived: true;
|
|
1331
|
+
}>;
|
|
1332
|
+
/**
|
|
1333
|
+
* Un-archive a character. If the original name now collides with an
|
|
1334
|
+
* active row, the server auto-suffixes "(restored)" and returns the
|
|
1335
|
+
* effective name.
|
|
1336
|
+
*/
|
|
1337
|
+
restore(id: string): Promise<{
|
|
1338
|
+
id: string;
|
|
1339
|
+
name: string;
|
|
1340
|
+
}>;
|
|
1341
|
+
/**
|
|
1342
|
+
* Duplicate (fork) a character to a new row with a `"(copy)"` suffix.
|
|
1343
|
+
* Asset URLs are shared by reference — the new row can diverge by
|
|
1344
|
+
* regenerating any of them.
|
|
1345
|
+
*/
|
|
1346
|
+
duplicate(id: string, input?: DuplicateCharacterInput): Promise<{
|
|
1347
|
+
id: string;
|
|
1348
|
+
name: string;
|
|
1349
|
+
}>;
|
|
1350
|
+
/**
|
|
1351
|
+
* Count of the caller's workflows that reference this character. Powers the
|
|
1352
|
+
* library "Archive" confirmation modal in the editor.
|
|
1353
|
+
*/
|
|
1354
|
+
usage(id: string): Promise<CharacterUsage>;
|
|
1355
|
+
/**
|
|
1356
|
+
* Fire `POST /v1/generate-character` to produce one or more portrait
|
|
1357
|
+
* candidates. With `count > 1`, all jobs are reserved up-front before any
|
|
1358
|
+
* is enqueued — mid-batch failures roll back atomically.
|
|
1359
|
+
*
|
|
1360
|
+
* When `attachToCharacterId` is set, the worker writes the result directly
|
|
1361
|
+
* to the row's `source_image_url`; otherwise you must call
|
|
1362
|
+
* `approvePortrait()` after picking a candidate.
|
|
1363
|
+
*/
|
|
1364
|
+
generate(input: GenerateCharacterInput): Promise<GenerateCharacterResult>;
|
|
1365
|
+
/**
|
|
1366
|
+
* Fire `POST /v1/generate-character-asset` to produce a single
|
|
1367
|
+
* expression / pose / angle / lighting variant. When the studio path is
|
|
1368
|
+
* set (`attachToCharacterId` + `attachToColumn` + `attachName`), the
|
|
1369
|
+
* worker appends `{ name: attachName, url: <result> }` to the named
|
|
1370
|
+
* JSONB array column on completion.
|
|
1371
|
+
*/
|
|
1372
|
+
generateAsset(input: GenerateAssetInput): Promise<{
|
|
1373
|
+
jobId: string;
|
|
1374
|
+
}>;
|
|
1375
|
+
/**
|
|
1376
|
+
* Fire `POST /v1/generate-character-motion` to animate the character's
|
|
1377
|
+
* portrait into a motion clip. The result is appended to the character's
|
|
1378
|
+
* `motions[]` bucket when `attachToCharacterId` is set.
|
|
1379
|
+
*/
|
|
1380
|
+
generateMotion(input: GenerateMotionInput): Promise<{
|
|
1381
|
+
jobId: string;
|
|
1382
|
+
}>;
|
|
1383
|
+
/**
|
|
1384
|
+
* Approve a completed `generate-character` job as the character's portrait.
|
|
1385
|
+
* Sets `source_image_url` and fires the LLM caption (Claude Sonnet vision)
|
|
1386
|
+
* inline. Returns the new portrait URL plus the caption — `canonicalDescription`
|
|
1387
|
+
* is `null` if the LLM call sub-failed (portrait still set; retry via `recaption()`).
|
|
1388
|
+
*/
|
|
1389
|
+
approvePortrait(id: string, candidateJobId: string): Promise<ApprovePortraitResult>;
|
|
1390
|
+
/**
|
|
1391
|
+
* Re-fire the LLM caption against the character's current portrait. 502s on
|
|
1392
|
+
* LLM failure; returns 400 `no_portrait` if no portrait is set yet.
|
|
1393
|
+
*/
|
|
1394
|
+
recaption(id: string): Promise<RecaptionResult>;
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
/**
|
|
1398
|
+
* Collapse a PersonValue into the comma-joined seed-prompt fragment used by
|
|
1399
|
+
* `characters.generate({ seedPrompt })` — same composition the Nodaro editor
|
|
1400
|
+
* performs. Powered by @nodaro/prompts.
|
|
1401
|
+
*/
|
|
1402
|
+
declare function buildPersonSeedPrompt(value: PersonValue): string;
|
|
1403
|
+
|
|
1404
|
+
/**
|
|
1405
|
+
* Re-export the shared `LocationAssetType` / `LocationAttachColumn` unions and
|
|
1406
|
+
* their runtime tuples so SDK consumers don't have to add `@nodaro/shared` as a
|
|
1407
|
+
* second dependency just to typecheck the `assetType` / `attachToColumn`
|
|
1408
|
+
* fields. Single source of truth lives in `@nodaro/shared/entity-prompts`.
|
|
1409
|
+
*
|
|
1410
|
+
* `CharacterAspectRatio` is re-exported alongside them — `generateMotion`'s
|
|
1411
|
+
* `aspectRatio` field reuses the same 4-value enum (1:1 / 3:4 / 16:9 / 9:16)
|
|
1412
|
+
* as characters; the route enforces this with `z.enum(CHARACTER_ASPECT_OPTIONS)`.
|
|
1413
|
+
*/
|
|
1414
|
+
/**
|
|
1415
|
+
* Reference-photo kind discriminator — the mood-board roles a user can attach
|
|
1416
|
+
* to a location. Mirrors the `reference_photos.kind` Zod enum in
|
|
1417
|
+
* `backend/src/routes/locations.ts`. `other` is the free-form bucket.
|
|
1418
|
+
*/
|
|
1419
|
+
type LocationReferencePhotoKind = "wide" | "interior" | "exterior" | "detail" | "moodBoard" | "other";
|
|
1420
|
+
interface LocationReferencePhoto {
|
|
1421
|
+
url: string;
|
|
1422
|
+
kind: LocationReferencePhotoKind;
|
|
1423
|
+
}
|
|
1424
|
+
/**
|
|
1425
|
+
* A location record returned by Nodaro's REST API. Mirrors the camelCase
|
|
1426
|
+
* shape produced by `backend/src/routes/locations.ts::toCamel()`.
|
|
1427
|
+
*
|
|
1428
|
+
* Asset buckets (`timeOfDay`, `weather`, `angles`, `lighting`, `seasons`,
|
|
1429
|
+
* `atmosphereMotions`) are independent JSONB arrays keyed by a human-readable
|
|
1430
|
+
* variant name (e.g. `"dawn"`, `"clear"`, `"wide"`). Each entry's `url` points
|
|
1431
|
+
* at an R2-hosted asset.
|
|
1432
|
+
*
|
|
1433
|
+
* Identity-foundation fields:
|
|
1434
|
+
* - `referencePhotos` — caller-supplied mood-board refs (cap 20).
|
|
1435
|
+
* - `canonicalDescription` — ~80–120-word LLM-authored visual caption,
|
|
1436
|
+
* populated by `approveMainImage()` / `recaption()`. The wire still sends
|
|
1437
|
+
* `""` on caption sub-failure (the breaking wire change is deferred to a
|
|
1438
|
+
* major bump), but `get()` normalizes `""` → `null` so consumers see the
|
|
1439
|
+
* same `string | null` semantics as characters.
|
|
1440
|
+
* - `styleLock` — whether asset gens should anchor to the canonical style
|
|
1441
|
+
* captured at approval time. Defaults to `true` on new rows.
|
|
1442
|
+
*/
|
|
1443
|
+
interface Location {
|
|
1444
|
+
id: string;
|
|
1445
|
+
userId: string;
|
|
1446
|
+
nodeId: string;
|
|
1447
|
+
projectId: string | null;
|
|
1448
|
+
name: string;
|
|
1449
|
+
description: string | null;
|
|
1450
|
+
category: string | null;
|
|
1451
|
+
style: string | null;
|
|
1452
|
+
sourceImageUrl: string | null;
|
|
1453
|
+
/** MODEL_CATALOG image-model id the main image was generated with (or `null`).
|
|
1454
|
+
* Set on create + editable via the update route. */
|
|
1455
|
+
imageProvider: string | null;
|
|
1456
|
+
timeOfDay: Array<{
|
|
1457
|
+
name: string;
|
|
1458
|
+
url: string;
|
|
1459
|
+
}>;
|
|
1460
|
+
weather: Array<{
|
|
1461
|
+
name: string;
|
|
1462
|
+
url: string;
|
|
1463
|
+
}>;
|
|
1464
|
+
angles: Array<{
|
|
1465
|
+
name: string;
|
|
1466
|
+
url: string;
|
|
1467
|
+
}>;
|
|
1468
|
+
lighting: Array<{
|
|
1469
|
+
name: string;
|
|
1470
|
+
url: string;
|
|
1471
|
+
}>;
|
|
1472
|
+
seasons: Array<{
|
|
1473
|
+
name: string;
|
|
1474
|
+
url: string;
|
|
1475
|
+
}>;
|
|
1476
|
+
atmosphereMotions: Array<{
|
|
1477
|
+
name: string;
|
|
1478
|
+
url: string;
|
|
1479
|
+
}>;
|
|
1480
|
+
/** Named Location Boards — dense reference sheets, one per variant/mood
|
|
1481
|
+
* (the `generate-image/location-board` factory preset rendered from the
|
|
1482
|
+
* location's images). A first-class bucket: community publish snapshots it
|
|
1483
|
+
* and clone hands the consumer their own copy to extend. Defaults to `[]`.
|
|
1484
|
+
* `type` marks an image-collage `"identity"` sheet vs a plain `"looks"`
|
|
1485
|
+
* board; `sourceImages` are the R2 URLs it was collaged from. Both optional
|
|
1486
|
+
* + backward-compatible (legacy boards have neither). */
|
|
1487
|
+
boards?: Array<{
|
|
1488
|
+
name: string;
|
|
1489
|
+
url: string;
|
|
1490
|
+
type?: "looks" | "identity";
|
|
1491
|
+
sourceImages?: string[];
|
|
1492
|
+
}> | null;
|
|
1493
|
+
referencePhotos: LocationReferencePhoto[];
|
|
1494
|
+
/** `null` when no caption is set (or the LLM caption sub-failed) — the wire
|
|
1495
|
+
* sends `""`, normalized to `null` in `get()` to match character semantics. */
|
|
1496
|
+
canonicalDescription: string | null;
|
|
1497
|
+
styleLock: boolean;
|
|
1498
|
+
/** The user's chosen DEFAULT asset take per variant (Studio version history).
|
|
1499
|
+
* OPAQUE map: key `"<bucket>:<variant>"` (e.g. `"timeOfDay:dawn"`) → the chosen
|
|
1500
|
+
* asset URL (one already present in that bucket). Stored verbatim — keys are
|
|
1501
|
+
* NOT normalized; soft-capped server-side at 200 keys / 2048-char values
|
|
1502
|
+
* (overflow dropped silently). Defaults to `{}`. */
|
|
1503
|
+
selectedAssetByVariant?: Record<string, string> | null;
|
|
1504
|
+
deletedAt: string | null;
|
|
1505
|
+
createdAt: string;
|
|
1506
|
+
updatedAt: string;
|
|
1507
|
+
}
|
|
1508
|
+
/**
|
|
1509
|
+
* GET /v1/locations/:id appends a `pendingJobs` bucket the studio uses to
|
|
1510
|
+
* rehydrate spinners after a reload. Optional on the SDK surface — it doesn't
|
|
1511
|
+
* appear on `list()` rows.
|
|
1512
|
+
*/
|
|
1513
|
+
interface LocationDetail extends Location {
|
|
1514
|
+
pendingJobs?: Array<{
|
|
1515
|
+
jobId: string;
|
|
1516
|
+
assetType: string;
|
|
1517
|
+
name: string;
|
|
1518
|
+
status: string;
|
|
1519
|
+
}>;
|
|
1520
|
+
/**
|
|
1521
|
+
* Completed candidate main images for this location whose URL differs from
|
|
1522
|
+
* the current `sourceImageUrl`, newest first (max 5). Surfaced so a
|
|
1523
|
+
* "pick from N" UI can offer alternatives the user can promote via
|
|
1524
|
+
* `approveMainImage(id, jobId)`; until they pick, the current main image is
|
|
1525
|
+
* left untouched. Mirrors `CharacterDetail.previousCandidates`. Optional -
|
|
1526
|
+
* absent on `list()` rows, present (possibly empty) on `get()`.
|
|
1527
|
+
*/
|
|
1528
|
+
previousCandidates?: Array<{
|
|
1529
|
+
jobId: string;
|
|
1530
|
+
url: string;
|
|
1531
|
+
createdAt: string;
|
|
1532
|
+
}>;
|
|
1533
|
+
}
|
|
1534
|
+
/**
|
|
1535
|
+
* Body for `client.locations.create()`. Mirrors the INSERT branch of
|
|
1536
|
+
* `upsertLocationBody` in `backend/src/routes/locations.ts`. `name` + `nodeId`
|
|
1537
|
+
* are required on create.
|
|
1538
|
+
*/
|
|
1539
|
+
interface CreateLocationInput {
|
|
1540
|
+
nodeId: string;
|
|
1541
|
+
name: string;
|
|
1542
|
+
description?: string;
|
|
1543
|
+
category?: string;
|
|
1544
|
+
style?: string;
|
|
1545
|
+
workflowId?: string;
|
|
1546
|
+
projectId?: string;
|
|
1547
|
+
sourceImageUrl?: string;
|
|
1548
|
+
/** Persistent image-model id (a MODEL_CATALOG image model). Validated
|
|
1549
|
+
* server-side — unknown / non-image / "" is stored as `null`. */
|
|
1550
|
+
imageProvider?: string | null;
|
|
1551
|
+
referencePhotos?: LocationReferencePhoto[];
|
|
1552
|
+
canonicalDescription?: string;
|
|
1553
|
+
styleLock?: boolean;
|
|
1554
|
+
/** The user's chosen DEFAULT asset take per variant. OPAQUE
|
|
1555
|
+
* `"<bucket>:<variant>"` → chosen-URL map; a write REPLACES the whole map.
|
|
1556
|
+
* Keys stored verbatim; soft-capped server-side at 200 keys / 2048-char values. */
|
|
1557
|
+
selectedAssetByVariant?: Record<string, string>;
|
|
1558
|
+
}
|
|
1559
|
+
/**
|
|
1560
|
+
* Body for `client.locations.update()`. Mirrors the UPDATE branch of
|
|
1561
|
+
* `upsertLocationBody` in `backend/src/routes/locations.ts`.
|
|
1562
|
+
*
|
|
1563
|
+
* Worker-owned asset buckets (`timeOfDay`/`weather`/`angles`/`lighting`/
|
|
1564
|
+
* `seasons`/`atmosphereMotions`) are deliberately omitted — the route drops
|
|
1565
|
+
* them on UPDATE so a Studio auto-save with a stale snapshot cannot clobber
|
|
1566
|
+
* the worker's atomic `append_location_asset()` writes.
|
|
1567
|
+
*
|
|
1568
|
+
* `expectedUpdatedAt` is the optimistic-concurrency token: when present, the
|
|
1569
|
+
* UPDATE only succeeds if the row's `updated_at` still matches; on mismatch
|
|
1570
|
+
* the route returns 409 so the studio can re-fetch + merge.
|
|
1571
|
+
*/
|
|
1572
|
+
interface UpdateLocationInput {
|
|
1573
|
+
name?: string;
|
|
1574
|
+
description?: string;
|
|
1575
|
+
category?: string;
|
|
1576
|
+
style?: string;
|
|
1577
|
+
sourceImageUrl?: string;
|
|
1578
|
+
/** Persistent image-model id (a MODEL_CATALOG image model). Validated
|
|
1579
|
+
* server-side — unknown / non-image / "" is stored as `null`. */
|
|
1580
|
+
imageProvider?: string | null;
|
|
1581
|
+
referencePhotos?: LocationReferencePhoto[];
|
|
1582
|
+
canonicalDescription?: string;
|
|
1583
|
+
styleLock?: boolean;
|
|
1584
|
+
/** The user's chosen DEFAULT asset take per variant. OPAQUE
|
|
1585
|
+
* `"<bucket>:<variant>"` → chosen-URL map; a write REPLACES the whole map
|
|
1586
|
+
* (omit to leave untouched). Keys stored verbatim; soft-capped server-side
|
|
1587
|
+
* at 200 keys / 2048-char values. */
|
|
1588
|
+
selectedAssetByVariant?: Record<string, string>;
|
|
1589
|
+
/** Named Location Boards (see `Location.boards`) — whole-array replace,
|
|
1590
|
+
* USER-owned (unlike the worker-owned buckets it flows through UPDATE).
|
|
1591
|
+
* Server caps: 24 boards, 200-char names, 30 sourceImages per board. */
|
|
1592
|
+
boards?: Array<{
|
|
1593
|
+
name: string;
|
|
1594
|
+
url: string;
|
|
1595
|
+
type?: "looks" | "identity";
|
|
1596
|
+
sourceImages?: string[];
|
|
1597
|
+
}>;
|
|
1598
|
+
/** ISO-8601 timestamp recording when PII consent was captured for this location. */
|
|
1599
|
+
piiConsentAt?: string;
|
|
1600
|
+
expectedUpdatedAt?: string;
|
|
1601
|
+
}
|
|
1602
|
+
interface UpdateLocationResult {
|
|
1603
|
+
id: string;
|
|
1604
|
+
updatedAt: string;
|
|
1605
|
+
}
|
|
1606
|
+
interface ListLocationsParams {
|
|
1607
|
+
/** When true, return archived locations instead of active ones. */
|
|
1608
|
+
archived?: boolean;
|
|
1609
|
+
}
|
|
1610
|
+
/**
|
|
1611
|
+
* Input for `client.locations.generate()` — fires the
|
|
1612
|
+
* `POST /v1/generate-location` route. Produces 1–10 candidate
|
|
1613
|
+
* establishing shots; each lands as one `jobs` row in `pending` state and
|
|
1614
|
+
* is then enqueued for the worker.
|
|
1615
|
+
*
|
|
1616
|
+
* When `attachToLocationId` is set AND `count === 1`, the worker writes the
|
|
1617
|
+
* resulting URL directly to `locations.source_image_url` on completion —
|
|
1618
|
+
* caller doesn't need a separate `approveMainImage` call. Multi-candidate
|
|
1619
|
+
* batches MUST go through explicit approval so the user picks the winner.
|
|
1620
|
+
*/
|
|
1621
|
+
interface GenerateLocationInput {
|
|
1622
|
+
name: string;
|
|
1623
|
+
description?: string;
|
|
1624
|
+
userPrompt?: string;
|
|
1625
|
+
category?: "indoor" | "outdoor" | "urban" | "nature" | "fantasy" | "sci-fi" | "historical" | "futuristic" | "other";
|
|
1626
|
+
style?: "realistic" | "anime" | "3d-pixar" | "illustration";
|
|
1627
|
+
sourceImageUrl?: string;
|
|
1628
|
+
provider?: string;
|
|
1629
|
+
/** Number of candidate images to generate (1–10; server-validated). */
|
|
1630
|
+
count?: number;
|
|
1631
|
+
/** Auto-attach the result to this location row (single-candidate only). */
|
|
1632
|
+
attachToLocationId?: string;
|
|
1633
|
+
/**
|
|
1634
|
+
* Credit-affecting quality tier (e.g. `"high"` for gpt-image). Priced like
|
|
1635
|
+
* generate-image (composite ids such as `gpt-image:high`); values the chosen
|
|
1636
|
+
* model doesn't support are ignored server-side, never rejected.
|
|
1637
|
+
*/
|
|
1638
|
+
quality?: string;
|
|
1639
|
+
/**
|
|
1640
|
+
* Credit-affecting output resolution (e.g. `"2K"` / `"4K"` / `"2 MP"`).
|
|
1641
|
+
* Priced like generate-image (composite ids such as `nano-banana-pro:4K`);
|
|
1642
|
+
* values the chosen model doesn't support are ignored server-side, never
|
|
1643
|
+
* rejected.
|
|
1644
|
+
*/
|
|
1645
|
+
resolution?: string;
|
|
1646
|
+
}
|
|
1647
|
+
/**
|
|
1648
|
+
* `generate()` response — `jobIds` is ALWAYS present (the harmonized contract,
|
|
1649
|
+
* matching characters). `jobId` is a deprecated back-compat alias populated only
|
|
1650
|
+
* on `count === 1`; prefer `jobIds`. (Will be removed on the next major.)
|
|
1651
|
+
*/
|
|
1652
|
+
interface GenerateLocationResult {
|
|
1653
|
+
jobIds: string[];
|
|
1654
|
+
/** @deprecated count===1 back-compat alias — use `jobIds`. */
|
|
1655
|
+
jobId?: string;
|
|
1656
|
+
}
|
|
1657
|
+
/**
|
|
1658
|
+
* Input for `client.locations.generateAsset()` — fires the
|
|
1659
|
+
* `POST /v1/generate-location-asset` route. Produces a single
|
|
1660
|
+
* timeOfDay / weather / seasons / angles / lighting / custom variant.
|
|
1661
|
+
*
|
|
1662
|
+
* When all three studio-path fields are set (`attachToLocationId` +
|
|
1663
|
+
* `attachToColumn` + `attachName`), the worker appends
|
|
1664
|
+
* `{ name: attachName, url: <result> }` to the named JSONB array column on
|
|
1665
|
+
* the user's location row on completion. `attachToColumn` is REQUIRED for
|
|
1666
|
+
* `assetType === "custom"` — the worker can't infer the bucket from the
|
|
1667
|
+
* asset type.
|
|
1668
|
+
*/
|
|
1669
|
+
interface GenerateLocationAssetInput {
|
|
1670
|
+
assetType: LocationAssetType;
|
|
1671
|
+
variant: string;
|
|
1672
|
+
name: string;
|
|
1673
|
+
description?: string;
|
|
1674
|
+
userPrompt?: string;
|
|
1675
|
+
category?: string;
|
|
1676
|
+
style?: "realistic" | "anime" | "3d-pixar" | "illustration";
|
|
1677
|
+
sourceImageUrl?: string;
|
|
1678
|
+
provider?: string;
|
|
1679
|
+
/** Optional framing override (the same 4-value enum as `generateMotion`).
|
|
1680
|
+
* Absent = the image model's default. The studio's 360° surround path pins
|
|
1681
|
+
* `"16:9"` so every ring view matches the establishing shot's frame. */
|
|
1682
|
+
aspectRatio?: CharacterAspectRatio;
|
|
1683
|
+
attachToLocationId?: string;
|
|
1684
|
+
attachToColumn?: LocationAttachColumn;
|
|
1685
|
+
attachName?: string;
|
|
1686
|
+
/**
|
|
1687
|
+
* Credit-affecting quality tier (e.g. `"high"` for gpt-image). Values the
|
|
1688
|
+
* chosen model doesn't support are ignored server-side, never rejected.
|
|
1689
|
+
*/
|
|
1690
|
+
quality?: string;
|
|
1691
|
+
/**
|
|
1692
|
+
* Credit-affecting output resolution (e.g. `"2K"` / `"4K"` / `"2 MP"`).
|
|
1693
|
+
* Values the chosen model doesn't support are ignored server-side, never
|
|
1694
|
+
* rejected.
|
|
1695
|
+
*/
|
|
1696
|
+
resolution?: string;
|
|
1697
|
+
}
|
|
1698
|
+
/**
|
|
1699
|
+
* Input for `client.locations.generateSurroundContinuation()` — fires the
|
|
1700
|
+
* `POST /v1/generate-surround-continuation` route. Generates one seamless 360°
|
|
1701
|
+
* ring view as an image-to-image continuation of `referenceImageUrl` (the
|
|
1702
|
+
* previous ring view, or the establishing shot for the first ring).
|
|
1703
|
+
*
|
|
1704
|
+
* The platform owns the whole pipeline: it builds the half-carry composite
|
|
1705
|
+
* server-side (carry the reference's trailing half into the new frame's leading
|
|
1706
|
+
* half per `direction`, gray the rest), paints the gray region, then
|
|
1707
|
+
* color-harmonizes the painted half to the carried half so there is no tonal
|
|
1708
|
+
* seam down the frame's center. The carried half stays pixel-exact, so a
|
|
1709
|
+
* panorama viewer stitching adjacent ring views stays geometrically seamless.
|
|
1710
|
+
*
|
|
1711
|
+
* When the studio path is set (`attachToLocationId` + `attachToColumn` +
|
|
1712
|
+
* `attachName`), the worker appends `{ name: attachName, url: <result> }` to the
|
|
1713
|
+
* location's bucket (studio uses `attachToColumn: "angles"`,
|
|
1714
|
+
* `attachName: "Surround 45°"`).
|
|
1715
|
+
*/
|
|
1716
|
+
interface GenerateSurroundContinuationInput {
|
|
1717
|
+
/** The previous ring view to continue from (i2i anchor). */
|
|
1718
|
+
referenceImageUrl: string;
|
|
1719
|
+
/** Carry/paint axis: turn right, turn left, tilt up, or tilt down. Tilts
|
|
1720
|
+
* render the sky/ground (a thin horizon strip), not a horizontal continuation. */
|
|
1721
|
+
direction: SurroundDirection;
|
|
1722
|
+
/** Ring angle (45, 90, …) — stored on the result as metadata. */
|
|
1723
|
+
degrees?: number;
|
|
1724
|
+
/** Fraction of the frame carried from the reference. Omitted ⇒ per-direction
|
|
1725
|
+
* default (0.5 for a pan, 0.12 thin strip for a tilt). */
|
|
1726
|
+
carriedFraction?: number;
|
|
1727
|
+
/** Upscale/denoise the result before it's chained as the next reference, to
|
|
1728
|
+
* slow cumulative softening down a long ring chain. Default false. */
|
|
1729
|
+
refine?: boolean;
|
|
1730
|
+
/** Refine model when `refine` is set. `recraft-upscale` (1 cr, default) or
|
|
1731
|
+
* `topaz-image-upscale` (3 cr). */
|
|
1732
|
+
refineProvider?: "recraft-upscale" | "topaz-image-upscale";
|
|
1733
|
+
/** Optional free-form scene hint woven into the fill prompt. */
|
|
1734
|
+
userPrompt?: string;
|
|
1735
|
+
/** Image model. Studio pins `nano-banana-pro`; default `nano-banana`. */
|
|
1736
|
+
provider?: string;
|
|
1737
|
+
/** Studio pins `"16:9"` so every ring view matches the establishing frame. */
|
|
1738
|
+
aspectRatio?: CharacterAspectRatio;
|
|
1739
|
+
attachToLocationId?: string;
|
|
1740
|
+
attachToColumn?: LocationAttachColumn;
|
|
1741
|
+
attachName?: string;
|
|
1742
|
+
}
|
|
1743
|
+
/**
|
|
1744
|
+
* Input for `client.locations.generateMotion()` — fires the
|
|
1745
|
+
* `POST /v1/generate-location-motion` route. Produces a single atmospheric
|
|
1746
|
+
* motion clip (drifting fog, snowfall, rolling waves, etc.) animated FROM a
|
|
1747
|
+
* static establishing-shot image.
|
|
1748
|
+
*
|
|
1749
|
+
* Mirrors `client.characters.generateMotion()` minus the character-specific
|
|
1750
|
+
* fields (gender / baseOutfit / realLifeRefs). The route hardcodes the attach
|
|
1751
|
+
* column to `atmosphere_motions` — callers supply `attachToLocationId` +
|
|
1752
|
+
* `attachName` only.
|
|
1753
|
+
*
|
|
1754
|
+
* `sourceImageUrl` is REQUIRED — image-to-video needs a source frame and the
|
|
1755
|
+
* route has no fallback (no `source_image_url` column to pull from on the
|
|
1756
|
+
* locations row; the studio path supplies the canonical establishing-shot URL
|
|
1757
|
+
* explicitly).
|
|
1758
|
+
*
|
|
1759
|
+
* When the studio path is set (`attachToLocationId` + `attachName`), the
|
|
1760
|
+
* worker appends `{ name: attachName, url: <result> }` to the location row's
|
|
1761
|
+
* `atmosphere_motions` JSONB column on completion.
|
|
1762
|
+
*/
|
|
1763
|
+
interface GenerateLocationMotionInput {
|
|
1764
|
+
motionPrompt: string;
|
|
1765
|
+
sourceImageUrl: string;
|
|
1766
|
+
provider?: string;
|
|
1767
|
+
name: string;
|
|
1768
|
+
category?: string;
|
|
1769
|
+
style?: "realistic" | "anime" | "3d-pixar" | "illustration";
|
|
1770
|
+
canonicalDescription?: string;
|
|
1771
|
+
attachToLocationId?: string;
|
|
1772
|
+
attachName?: string;
|
|
1773
|
+
/**
|
|
1774
|
+
* Optional aspect ratio override. Defaults to 16:9 server-side via
|
|
1775
|
+
* `resolveLocationAspectRatio` (locations are cinematic establishing shots).
|
|
1776
|
+
* One of the 4-value `CharacterAspectRatio` union — locations reuse the
|
|
1777
|
+
* character aspect enum since the supported ratios are identical.
|
|
1778
|
+
*/
|
|
1779
|
+
aspectRatio?: CharacterAspectRatio;
|
|
1780
|
+
}
|
|
1781
|
+
interface ApproveMainImageResult {
|
|
1782
|
+
sourceImageUrl: string;
|
|
1783
|
+
/**
|
|
1784
|
+
* LLM-authored caption. `null` when the LLM caption sub-failed — the wire
|
|
1785
|
+
* sends `""`, normalized to `null` here so consumers see the same
|
|
1786
|
+
* `string | null` semantics as characters. The main image is still set; call
|
|
1787
|
+
* `recaption()` to retry.
|
|
1788
|
+
*/
|
|
1789
|
+
canonicalDescription: string | null;
|
|
1790
|
+
}
|
|
1791
|
+
interface RecaptionLocationResult {
|
|
1792
|
+
canonicalDescription: string;
|
|
1793
|
+
}
|
|
1794
|
+
declare class LocationsResource {
|
|
1795
|
+
private client;
|
|
1796
|
+
constructor(client: NodaroClient);
|
|
1797
|
+
/**
|
|
1798
|
+
* List the caller's locations. By default returns active locations only;
|
|
1799
|
+
* pass `archived: true` to fetch soft-deleted rows for an "archive" view.
|
|
1800
|
+
*/
|
|
1801
|
+
list(params?: ListLocationsParams): Promise<{
|
|
1802
|
+
locations: Location[];
|
|
1803
|
+
}>;
|
|
1804
|
+
/**
|
|
1805
|
+
* Convenience wrapper for `list({ archived: true })`. Returns soft-deleted
|
|
1806
|
+
* rows so callers can drive a UI "Archived" tab without re-encoding the
|
|
1807
|
+
* query param. Mirrors `ObjectsResource.listArchived`.
|
|
1808
|
+
*
|
|
1809
|
+
* `archived` is omitted from the param type — it's always set to `true` here.
|
|
1810
|
+
*/
|
|
1811
|
+
listArchived(params?: Omit<ListLocationsParams, "archived">): Promise<{
|
|
1812
|
+
locations: Location[];
|
|
1813
|
+
}>;
|
|
1814
|
+
/**
|
|
1815
|
+
* Fetch a single location including in-flight asset job state. Soft-deleted
|
|
1816
|
+
* (archived) rows are returned by id intentionally so canvas nodes that
|
|
1817
|
+
* hold a stale `locationDbId` keep loading.
|
|
1818
|
+
*/
|
|
1819
|
+
get(id: string): Promise<LocationDetail>;
|
|
1820
|
+
/**
|
|
1821
|
+
* Create a new location. `name` + `nodeId` are required — the route 400s
|
|
1822
|
+
* otherwise. Returns the new row's id.
|
|
1823
|
+
*
|
|
1824
|
+
* Note: the underlying route is the same `POST /v1/locations` upsert that
|
|
1825
|
+
* powers `update()`. This convenience wrapper enforces the INSERT-required
|
|
1826
|
+
* fields at the type level and never sends an `id`.
|
|
1827
|
+
*/
|
|
1828
|
+
create(data: CreateLocationInput): Promise<{
|
|
1829
|
+
id: string;
|
|
1830
|
+
}>;
|
|
1831
|
+
/**
|
|
1832
|
+
* Update a location. Only the fields you pass are written — undefined keys
|
|
1833
|
+
* are NOT touched on the row. Worker-owned asset buckets are intentionally
|
|
1834
|
+
* not exposed on this surface (see `UpdateLocationInput` for the rationale).
|
|
1835
|
+
*
|
|
1836
|
+
* Optimistic-concurrency: pass `expectedUpdatedAt` to require the row's
|
|
1837
|
+
* `updated_at` still matches; on mismatch the route returns 409
|
|
1838
|
+
* `concurrent_modification`. The SDK surfaces that as a generic
|
|
1839
|
+
* `NodaroError` with the same code.
|
|
1840
|
+
*/
|
|
1841
|
+
update(id: string, data: UpdateLocationInput): Promise<UpdateLocationResult>;
|
|
1842
|
+
/**
|
|
1843
|
+
* Soft-delete (archive) a location. The row is hidden from `list()` by
|
|
1844
|
+
* default but still loadable via `get(id)` so canvas nodes pointing at it
|
|
1845
|
+
* keep working. Restore with `restore(id)`.
|
|
1846
|
+
*/
|
|
1847
|
+
delete(id: string): Promise<{
|
|
1848
|
+
success: true;
|
|
1849
|
+
archived: true;
|
|
1850
|
+
}>;
|
|
1851
|
+
/**
|
|
1852
|
+
* Un-archive a location. If the original name now collides (case-
|
|
1853
|
+
* insensitive) with an active row, the server auto-suffixes "(restored)"
|
|
1854
|
+
* and returns the effective name.
|
|
1855
|
+
*/
|
|
1856
|
+
restore(id: string): Promise<{
|
|
1857
|
+
id: string;
|
|
1858
|
+
name: string;
|
|
1859
|
+
}>;
|
|
1860
|
+
/**
|
|
1861
|
+
* Fire `POST /v1/generate-location` to produce one or more candidate main
|
|
1862
|
+
* images. With `count > 1`, all jobs are reserved up-front before any
|
|
1863
|
+
* is enqueued — mid-batch failures roll back atomically.
|
|
1864
|
+
*
|
|
1865
|
+
* When `attachToLocationId` is set AND `count === 1`, the worker writes
|
|
1866
|
+
* the result directly to the row's `source_image_url`; otherwise you must
|
|
1867
|
+
* call `approveMainImage()` after picking a candidate.
|
|
1868
|
+
*/
|
|
1869
|
+
generate(data: GenerateLocationInput): Promise<GenerateLocationResult>;
|
|
1870
|
+
/**
|
|
1871
|
+
* Fire `POST /v1/generate-location-asset` to produce a single variant.
|
|
1872
|
+
* When the studio path is set (`attachToLocationId` + `attachToColumn` +
|
|
1873
|
+
* `attachName`), the worker appends `{ name: attachName, url: <result> }`
|
|
1874
|
+
* to the named JSONB array column on completion.
|
|
1875
|
+
*/
|
|
1876
|
+
generateAsset(data: GenerateLocationAssetInput): Promise<{
|
|
1877
|
+
jobId: string;
|
|
1878
|
+
}>;
|
|
1879
|
+
/**
|
|
1880
|
+
* Fire `POST /v1/generate-surround-continuation` to produce one seamless 360°
|
|
1881
|
+
* ring view as an i2i continuation of `referenceImageUrl`. The platform builds
|
|
1882
|
+
* the half-carry composite, paints the missing half, and color-harmonizes it
|
|
1883
|
+
* to the carried half (no tonal seam; carried half stays pixel-exact). When the
|
|
1884
|
+
* studio path is set, the worker appends the result to the location's bucket.
|
|
1885
|
+
*/
|
|
1886
|
+
generateSurroundContinuation(data: GenerateSurroundContinuationInput): Promise<{
|
|
1887
|
+
jobId: string;
|
|
1888
|
+
}>;
|
|
1889
|
+
/**
|
|
1890
|
+
* Fire `POST /v1/generate-location-motion` to animate the location's
|
|
1891
|
+
* establishing shot into an atmospheric motion clip. Image-to-video, single
|
|
1892
|
+
* clip per call; the attach column is hardcoded to `atmosphere_motions`
|
|
1893
|
+
* server-side (locations have a single motion bucket so the caller doesn't
|
|
1894
|
+
* supply `attachToColumn`). When the studio path is set
|
|
1895
|
+
* (`attachToLocationId` + `attachName`), the worker appends
|
|
1896
|
+
* `{ name: attachName, url: <result> }` to the row's `atmosphere_motions`
|
|
1897
|
+
* column on completion.
|
|
1898
|
+
*/
|
|
1899
|
+
generateMotion(data: GenerateLocationMotionInput): Promise<{
|
|
1900
|
+
jobId: string;
|
|
1901
|
+
}>;
|
|
1902
|
+
/**
|
|
1903
|
+
* Atomically remove ONE asset take (every entry matching `url`) from a
|
|
1904
|
+
* worker-owned bucket column — `POST /v1/locations/:id/remove-asset`. The
|
|
1905
|
+
* worker-owned buckets are deliberately not writable through `update()`
|
|
1906
|
+
* (a stale snapshot would race concurrent worker appends), so deleting a
|
|
1907
|
+
* take — e.g. a 360° surround view being regenerated — goes through this
|
|
1908
|
+
* single-statement server-side filter instead. 404s (`NotFoundError`) when
|
|
1909
|
+
* the url isn't in that bucket or the location isn't yours.
|
|
1910
|
+
*/
|
|
1911
|
+
removeAsset(id: string, data: {
|
|
1912
|
+
column: LocationAttachColumn;
|
|
1913
|
+
url: string;
|
|
1914
|
+
}): Promise<{
|
|
1915
|
+
removed: true;
|
|
1916
|
+
}>;
|
|
1917
|
+
/**
|
|
1918
|
+
* Approve a completed `generate-location` job as the location's main image.
|
|
1919
|
+
* Sets `source_image_url` and fires the LLM caption (Claude Sonnet vision)
|
|
1920
|
+
* inline. Returns the new main-image URL plus the caption.
|
|
1921
|
+
*
|
|
1922
|
+
* Caption-failure semantics: the route still sends `""` on LLM sub-failure,
|
|
1923
|
+
* but the SDK normalizes `""` → `null` here so `canonicalDescription` carries
|
|
1924
|
+
* the same `string | null` semantics as characters. The main image is still
|
|
1925
|
+
* set; call `recaption()` to retry.
|
|
1926
|
+
*/
|
|
1927
|
+
approveMainImage(id: string, candidateJobId: string): Promise<ApproveMainImageResult>;
|
|
1928
|
+
/**
|
|
1929
|
+
* Re-fire the LLM caption against the location's current main image. 502s
|
|
1930
|
+
* on LLM failure (unlike `approveMainImage` which preserves the side-effect
|
|
1931
|
+
* and returns ""); returns 400 `no_source_image` if no main image is set
|
|
1932
|
+
* yet.
|
|
1933
|
+
*/
|
|
1934
|
+
recaption(id: string): Promise<RecaptionLocationResult>;
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
/**
|
|
1938
|
+
* Re-export the shared `ObjectAssetType` / `ObjectAttachColumn` unions and
|
|
1939
|
+
* their runtime tuples so SDK consumers don't have to add `@nodaro/shared` as
|
|
1940
|
+
* a second dependency just to typecheck the `assetType` / `attachToColumn`
|
|
1941
|
+
* fields. Single source of truth lives in `@nodaro/shared/entity-prompts`.
|
|
1942
|
+
*
|
|
1943
|
+
* `ObjectAspectRatio` is re-exported alongside them — `generateMotion`'s
|
|
1944
|
+
* `aspectRatio` field is the 5-value object enum (1:1 / 3:4 / 16:9 / 9:16 /
|
|
1945
|
+
* 4:3) from `@nodaro/shared/object-aspect-defaults`. Distinct from
|
|
1946
|
+
* `CharacterAspectRatio` because objects support an extra 4:3 framing for
|
|
1947
|
+
* product-showcase shots.
|
|
1948
|
+
*/
|
|
1949
|
+
/**
|
|
1950
|
+
* Reference-photo kind discriminator — the mood-board roles a user can attach
|
|
1951
|
+
* to an object. Mirrors the `reference_photos.kind` field accepted by
|
|
1952
|
+
* `backend/src/routes/objects.ts` (the route accepts open strings; this SDK
|
|
1953
|
+
* type narrows to the 6 canonical roles surfaced by the Studio). `other` is
|
|
1954
|
+
* the free-form bucket.
|
|
1955
|
+
*/
|
|
1956
|
+
type ObjectReferencePhotoKind = "front" | "side" | "detail" | "context" | "moodBoard" | "other";
|
|
1957
|
+
interface ObjectReferencePhoto {
|
|
1958
|
+
url: string;
|
|
1959
|
+
kind: ObjectReferencePhotoKind;
|
|
1960
|
+
}
|
|
1961
|
+
/**
|
|
1962
|
+
* An object record returned by Nodaro's REST API. Mirrors the camelCase
|
|
1963
|
+
* shape produced by `backend/src/routes/objects.ts::toCamel()`.
|
|
1964
|
+
*
|
|
1965
|
+
* Asset buckets (`angles`, `materials`, `variations`, `motionClips`) are
|
|
1966
|
+
* independent JSONB arrays keyed by a human-readable variant name (e.g.
|
|
1967
|
+
* `"front"`, `"wood"`, `"weathered"`). Each entry's `url` points at an
|
|
1968
|
+
* R2-hosted asset.
|
|
1969
|
+
*
|
|
1970
|
+
* Identity-foundation fields:
|
|
1971
|
+
* - `referencePhotos` — caller-supplied mood-board refs (cap 20). Objects
|
|
1972
|
+
* do NOT carry a `piiConsentAt` field (location Phase 2 #7 only).
|
|
1973
|
+
* - `canonicalDescription` — ~80–120-word LLM-authored visual caption,
|
|
1974
|
+
* populated by `approveMainImage()` / `recaption()`. The wire still sends
|
|
1975
|
+
* `""` on caption sub-failure (the breaking wire change is deferred to a
|
|
1976
|
+
* major bump), but `get()` normalizes `""` → `null` so consumers see the
|
|
1977
|
+
* same `string | null` semantics as characters.
|
|
1978
|
+
* - `styleLock` — whether asset gens should anchor to the canonical style
|
|
1979
|
+
* captured at approval time. Defaults to `true` on new rows.
|
|
1980
|
+
*
|
|
1981
|
+
* `Object` shadows the JS global, which TypeScript handles cleanly via
|
|
1982
|
+
* local-scope resolution. Consumers who need both can alias as
|
|
1983
|
+
* `import type { Object as NodaroObject } from "@nodaro/sdk"`.
|
|
1984
|
+
*/
|
|
1985
|
+
interface Object$1 {
|
|
1986
|
+
id: string;
|
|
1987
|
+
userId: string;
|
|
1988
|
+
nodeId: string;
|
|
1989
|
+
projectId: string | null;
|
|
1990
|
+
name: string;
|
|
1991
|
+
description: string | null;
|
|
1992
|
+
category: string | null;
|
|
1993
|
+
style: string | null;
|
|
1994
|
+
sourceImageUrl: string | null;
|
|
1995
|
+
/** MODEL_CATALOG image-model id the main image was generated with (or `null`).
|
|
1996
|
+
* Set on create + editable via the update route. */
|
|
1997
|
+
imageProvider: string | null;
|
|
1998
|
+
angles: Array<{
|
|
1999
|
+
name: string;
|
|
2000
|
+
url: string;
|
|
2001
|
+
}>;
|
|
2002
|
+
materials: Array<{
|
|
2003
|
+
name: string;
|
|
2004
|
+
url: string;
|
|
2005
|
+
}>;
|
|
2006
|
+
variations: Array<{
|
|
2007
|
+
name: string;
|
|
2008
|
+
url: string;
|
|
2009
|
+
}>;
|
|
2010
|
+
motionClips: Array<{
|
|
2011
|
+
name: string;
|
|
2012
|
+
url: string;
|
|
2013
|
+
}>;
|
|
2014
|
+
/** Named Product Boards — dense reference sheets, one per variant/colorway
|
|
2015
|
+
* (the `generate-image/product-board` factory preset rendered from the
|
|
2016
|
+
* object's images). A first-class bucket: community publish snapshots it
|
|
2017
|
+
* and clone hands the consumer their own copy to extend. Defaults to `[]`.
|
|
2018
|
+
* `type` marks an image-collage `"identity"` sheet vs a plain `"looks"`
|
|
2019
|
+
* board; `sourceImages` are the R2 URLs it was collaged from. Both optional
|
|
2020
|
+
* + backward-compatible (legacy boards have neither). */
|
|
2021
|
+
boards?: Array<{
|
|
2022
|
+
name: string;
|
|
2023
|
+
url: string;
|
|
2024
|
+
type?: "looks" | "identity";
|
|
2025
|
+
sourceImages?: string[];
|
|
2026
|
+
}> | null;
|
|
2027
|
+
referencePhotos: ObjectReferencePhoto[];
|
|
2028
|
+
/** `null` when no caption is set (or the LLM caption sub-failed) — the wire
|
|
2029
|
+
* sends `""`, normalized to `null` in `get()` to match character semantics. */
|
|
2030
|
+
canonicalDescription: string | null;
|
|
2031
|
+
styleLock: boolean;
|
|
2032
|
+
/** The user's chosen DEFAULT asset take per variant (Studio version history).
|
|
2033
|
+
* OPAQUE map: key `"<bucket>:<variant>"` (e.g. `"angles:front"`) → the chosen
|
|
2034
|
+
* asset URL (one already present in that bucket). Stored verbatim — keys are
|
|
2035
|
+
* NOT normalized; soft-capped server-side at 200 keys / 2048-char values
|
|
2036
|
+
* (overflow dropped silently). Defaults to `{}`. */
|
|
2037
|
+
selectedAssetByVariant?: Record<string, string> | null;
|
|
2038
|
+
deletedAt: string | null;
|
|
2039
|
+
createdAt: string;
|
|
2040
|
+
updatedAt: string;
|
|
2041
|
+
}
|
|
2042
|
+
/**
|
|
2043
|
+
* GET /v1/objects/:id may append a `pendingJobs` bucket the studio uses to
|
|
2044
|
+
* rehydrate spinners after a reload. Optional on the SDK surface — it doesn't
|
|
2045
|
+
* appear on `list()` rows.
|
|
2046
|
+
*/
|
|
2047
|
+
interface ObjectDetail extends Object$1 {
|
|
2048
|
+
pendingJobs?: Array<{
|
|
2049
|
+
jobId: string;
|
|
2050
|
+
assetType: string;
|
|
2051
|
+
name: string;
|
|
2052
|
+
status: string;
|
|
2053
|
+
}>;
|
|
2054
|
+
}
|
|
2055
|
+
/**
|
|
2056
|
+
* Body for `client.objects.create()`. Mirrors the INSERT branch of
|
|
2057
|
+
* `upsertObjectBody` in `backend/src/routes/objects.ts`. `name` + `nodeId`
|
|
2058
|
+
* are required on create.
|
|
2059
|
+
*/
|
|
2060
|
+
interface CreateObjectInput {
|
|
2061
|
+
nodeId: string;
|
|
2062
|
+
name: string;
|
|
2063
|
+
description?: string;
|
|
2064
|
+
category?: ObjectCategory;
|
|
2065
|
+
style?: string;
|
|
2066
|
+
workflowId?: string;
|
|
2067
|
+
projectId?: string;
|
|
2068
|
+
sourceImageUrl?: string;
|
|
2069
|
+
/** Persistent image-model id (a MODEL_CATALOG image model). Validated
|
|
2070
|
+
* server-side — unknown / non-image / "" is stored as `null`. */
|
|
2071
|
+
imageProvider?: string | null;
|
|
2072
|
+
referencePhotos?: ObjectReferencePhoto[];
|
|
2073
|
+
canonicalDescription?: string;
|
|
2074
|
+
styleLock?: boolean;
|
|
2075
|
+
/** The user's chosen DEFAULT asset take per variant. OPAQUE
|
|
2076
|
+
* `"<bucket>:<variant>"` → chosen-URL map; a write REPLACES the whole map.
|
|
2077
|
+
* Keys stored verbatim; soft-capped server-side at 200 keys / 2048-char values. */
|
|
2078
|
+
selectedAssetByVariant?: Record<string, string>;
|
|
2079
|
+
}
|
|
2080
|
+
/**
|
|
2081
|
+
* The 10-value Object category enum. Mirrors the literal accepted by
|
|
2082
|
+
* `POST /v1/generate-object` and surfaced in the Object Studio category
|
|
2083
|
+
* picker. Distinct from location's geography-based set.
|
|
2084
|
+
*/
|
|
2085
|
+
type ObjectCategory = "furniture" | "vehicle" | "weapon" | "food" | "clothing" | "electronics" | "nature" | "tool" | "animal" | "other";
|
|
2086
|
+
/**
|
|
2087
|
+
* Body for `client.objects.update()`. Mirrors the UPDATE branch of
|
|
2088
|
+
* `upsertObjectBody` in `backend/src/routes/objects.ts`.
|
|
2089
|
+
*
|
|
2090
|
+
* Worker-owned asset buckets (`angles` / `materials` / `variations` /
|
|
2091
|
+
* `motionClips`) are deliberately omitted — the route drops them on UPDATE
|
|
2092
|
+
* so a Studio auto-save with a stale snapshot cannot clobber the worker's
|
|
2093
|
+
* atomic `append_object_asset()` writes.
|
|
2094
|
+
*
|
|
2095
|
+
* `expectedUpdatedAt` is the optimistic-concurrency token: when present, the
|
|
2096
|
+
* UPDATE only succeeds if the row's `updated_at` still matches; on mismatch
|
|
2097
|
+
* the route returns 409 so the studio can re-fetch + merge.
|
|
2098
|
+
*/
|
|
2099
|
+
interface UpdateObjectInput {
|
|
2100
|
+
name?: string;
|
|
2101
|
+
description?: string;
|
|
2102
|
+
category?: ObjectCategory;
|
|
2103
|
+
style?: string;
|
|
2104
|
+
sourceImageUrl?: string;
|
|
2105
|
+
/** Persistent image-model id (a MODEL_CATALOG image model). Validated
|
|
2106
|
+
* server-side — unknown / non-image / "" is stored as `null`. */
|
|
2107
|
+
imageProvider?: string | null;
|
|
2108
|
+
referencePhotos?: ObjectReferencePhoto[];
|
|
2109
|
+
canonicalDescription?: string;
|
|
2110
|
+
styleLock?: boolean;
|
|
2111
|
+
/** The user's chosen DEFAULT asset take per variant. OPAQUE
|
|
2112
|
+
* `"<bucket>:<variant>"` → chosen-URL map; a write REPLACES the whole map
|
|
2113
|
+
* (omit to leave untouched). Keys stored verbatim; soft-capped server-side
|
|
2114
|
+
* at 200 keys / 2048-char values. */
|
|
2115
|
+
selectedAssetByVariant?: Record<string, string>;
|
|
2116
|
+
/** Named Product Boards (see `Object.boards`) — whole-array replace,
|
|
2117
|
+
* USER-owned (unlike the worker-owned buckets it flows through UPDATE).
|
|
2118
|
+
* Server caps: 24 boards, 200-char names, 30 sourceImages per board. */
|
|
2119
|
+
boards?: Array<{
|
|
2120
|
+
name: string;
|
|
2121
|
+
url: string;
|
|
2122
|
+
type?: "looks" | "identity";
|
|
2123
|
+
sourceImages?: string[];
|
|
2124
|
+
}>;
|
|
2125
|
+
expectedUpdatedAt?: string;
|
|
2126
|
+
}
|
|
2127
|
+
interface UpdateObjectResult {
|
|
2128
|
+
id: string;
|
|
2129
|
+
updatedAt: string;
|
|
2130
|
+
}
|
|
2131
|
+
/**
|
|
2132
|
+
* Combined create + update body (parameter for both branches). Exported for
|
|
2133
|
+
* callers that want to drive a single `upsert` flow without picking between
|
|
2134
|
+
* `Create*` and `Update*`. Mirrors the `upsertObjectBody` Zod schema in
|
|
2135
|
+
* `backend/src/routes/objects.ts`. `nodeId` + `name` are required on INSERT;
|
|
2136
|
+
* `id` flips the route into UPDATE mode.
|
|
2137
|
+
*/
|
|
2138
|
+
interface UpsertObjectInput extends CreateObjectInput {
|
|
2139
|
+
id?: string;
|
|
2140
|
+
expectedUpdatedAt?: string;
|
|
2141
|
+
}
|
|
2142
|
+
type UpsertObjectResult = {
|
|
2143
|
+
id: string;
|
|
2144
|
+
} | UpdateObjectResult;
|
|
2145
|
+
interface ListObjectsParams {
|
|
2146
|
+
/** When true, return archived objects instead of active ones. */
|
|
2147
|
+
archived?: boolean;
|
|
2148
|
+
/** Optional project filter — server-scoped to the caller's user. */
|
|
2149
|
+
projectId?: string;
|
|
2150
|
+
}
|
|
2151
|
+
/**
|
|
2152
|
+
* Input for `client.objects.generate()` — fires the
|
|
2153
|
+
* `POST /v1/generate-object` route. Produces 1–10 candidate
|
|
2154
|
+
* main images; each lands as one `jobs` row in `pending` state and is then
|
|
2155
|
+
* enqueued for the worker.
|
|
2156
|
+
*
|
|
2157
|
+
* When `attachToObjectId` is set AND `count === 1`, the worker writes the
|
|
2158
|
+
* resulting URL directly to `objects.source_image_url` on completion —
|
|
2159
|
+
* caller doesn't need a separate `approveMainImage` call. Multi-candidate
|
|
2160
|
+
* batches MUST go through explicit approval so the user picks the winner.
|
|
2161
|
+
*
|
|
2162
|
+
* `seedPromptHint` (Pass 7 F-77) flows the parameter-picker's prompt fragment
|
|
2163
|
+
* through to the worker so a catalog selection (e.g. "antique brass lantern")
|
|
2164
|
+
* gets appended to the generated prompt context.
|
|
2165
|
+
*/
|
|
2166
|
+
interface GenerateObjectInput {
|
|
2167
|
+
name: string;
|
|
2168
|
+
description?: string;
|
|
2169
|
+
userPrompt?: string;
|
|
2170
|
+
category?: ObjectCategory;
|
|
2171
|
+
style?: "realistic" | "anime" | "3d-pixar" | "illustration";
|
|
2172
|
+
sourceImageUrl?: string;
|
|
2173
|
+
provider?: string;
|
|
2174
|
+
/** Number of candidate images to generate (1–10; server-validated). */
|
|
2175
|
+
count?: number;
|
|
2176
|
+
/** Auto-attach the result to this object row (single-candidate only). */
|
|
2177
|
+
attachToObjectId?: string;
|
|
2178
|
+
/** Parameter-picker prompt-fragment pass-through. */
|
|
2179
|
+
seedPromptHint?: string;
|
|
2180
|
+
/** Optional name to set on the attached row alongside the main image. */
|
|
2181
|
+
attachName?: string;
|
|
2182
|
+
/** Optimistic-concurrency token for the single-candidate auto-attach path. */
|
|
2183
|
+
expectedUpdatedAt?: string;
|
|
2184
|
+
}
|
|
2185
|
+
/**
|
|
2186
|
+
* `generate()` response — `jobIds` is ALWAYS present (the harmonized contract,
|
|
2187
|
+
* matching characters). `jobId` is a deprecated back-compat alias populated only
|
|
2188
|
+
* on `count === 1`; prefer `jobIds`. (Will be removed on the next major.)
|
|
2189
|
+
*/
|
|
2190
|
+
interface GenerateObjectResult {
|
|
2191
|
+
jobIds: string[];
|
|
2192
|
+
/** @deprecated count===1 back-compat alias — use `jobIds`. */
|
|
2193
|
+
jobId?: string;
|
|
2194
|
+
}
|
|
2195
|
+
/**
|
|
2196
|
+
* Input for `client.objects.generateAsset()` — fires the
|
|
2197
|
+
* `POST /v1/generate-object-asset` route. Produces a single
|
|
2198
|
+
* angles / materials / variations / custom variant.
|
|
2199
|
+
*
|
|
2200
|
+
* When all three studio-path fields are set (`attachToObjectId` +
|
|
2201
|
+
* `attachToColumn` + `attachName`), the worker appends
|
|
2202
|
+
* `{ name: attachName, url: <result> }` to the named JSONB array column on
|
|
2203
|
+
* the user's object row on completion. `attachToColumn` is REQUIRED for
|
|
2204
|
+
* `assetType === "custom"` — the worker can't infer the bucket from the
|
|
2205
|
+
* asset type.
|
|
2206
|
+
*/
|
|
2207
|
+
interface GenerateObjectAssetInput {
|
|
2208
|
+
assetType: ObjectAssetType;
|
|
2209
|
+
variant: string;
|
|
2210
|
+
name: string;
|
|
2211
|
+
description?: string;
|
|
2212
|
+
userPrompt?: string;
|
|
2213
|
+
category?: string;
|
|
2214
|
+
style?: "realistic" | "anime" | "3d-pixar" | "illustration";
|
|
2215
|
+
sourceImageUrl?: string;
|
|
2216
|
+
provider?: string;
|
|
2217
|
+
attachToObjectId?: string;
|
|
2218
|
+
attachToColumn?: ObjectAttachColumn;
|
|
2219
|
+
attachName?: string;
|
|
2220
|
+
/** Parameter-picker prompt-fragment pass-through (Pass 7 F-77). */
|
|
2221
|
+
seedPromptHint?: string;
|
|
2222
|
+
}
|
|
2223
|
+
interface GenerateObjectAssetResult {
|
|
2224
|
+
jobId: string;
|
|
2225
|
+
}
|
|
2226
|
+
/**
|
|
2227
|
+
* Input for `client.objects.generateMotion()` — fires the
|
|
2228
|
+
* `POST /v1/generate-object-motion` route. Produces a single motion clip
|
|
2229
|
+
* (rotation, orbit, hover, drift, etc.) animated FROM a static product-shot
|
|
2230
|
+
* image.
|
|
2231
|
+
*
|
|
2232
|
+
* Mirrors `client.locations.generateMotion()` minus the location-specific
|
|
2233
|
+
* atmospheric fields. The route hardcodes the attach column to `motion_clips`
|
|
2234
|
+
* — callers supply `attachToObjectId` + `attachName` only.
|
|
2235
|
+
*
|
|
2236
|
+
* `sourceImageUrl` is REQUIRED — image-to-video needs a source frame and the
|
|
2237
|
+
* route has no fallback (no `source_image_url` column to pull from on the
|
|
2238
|
+
* objects row at this point in the flow; the studio path supplies the
|
|
2239
|
+
* canonical product-shot URL explicitly).
|
|
2240
|
+
*
|
|
2241
|
+
* Object-specific defaults vs location:
|
|
2242
|
+
* - `provider` defaults to `"kling-turbo"` (not location's `"kling"`)
|
|
2243
|
+
* - `aspectRatio` defaults to `"1:1"` server-side via
|
|
2244
|
+
* `resolveObjectAspectRatio({ assetType: "motion" })` — objects are
|
|
2245
|
+
* product-showcase framing, not cinematic establishing shots.
|
|
2246
|
+
*
|
|
2247
|
+
* When the studio path is set (`attachToObjectId` + `attachName`), the
|
|
2248
|
+
* worker appends `{ name: attachName, url: <result> }` to the object row's
|
|
2249
|
+
* `motion_clips` JSONB column on completion.
|
|
2250
|
+
*/
|
|
2251
|
+
interface GenerateObjectMotionInput {
|
|
2252
|
+
motionPrompt: string;
|
|
2253
|
+
sourceImageUrl: string;
|
|
2254
|
+
provider?: string;
|
|
2255
|
+
name: string;
|
|
2256
|
+
/** Source clip URL — when set, worker routes to video-to-video refine. */
|
|
2257
|
+
refineFromVideoUrl?: string;
|
|
2258
|
+
category?: string;
|
|
2259
|
+
style?: "realistic" | "anime" | "3d-pixar" | "illustration";
|
|
2260
|
+
canonicalDescription?: string;
|
|
2261
|
+
/** Parameter-picker prompt-fragment pass-through (Pass 7 F-77). */
|
|
2262
|
+
seedPromptHint?: string;
|
|
2263
|
+
attachToObjectId?: string;
|
|
2264
|
+
attachName?: string;
|
|
2265
|
+
/**
|
|
2266
|
+
* Optional aspect ratio override. Defaults to 1:1 server-side. One of the
|
|
2267
|
+
* 5-value `ObjectAspectRatio` union (1:1 / 3:4 / 16:9 / 9:16 / 4:3) —
|
|
2268
|
+
* objects have their own enum (with 4:3 added) vs the character set.
|
|
2269
|
+
*/
|
|
2270
|
+
aspectRatio?: ObjectAspectRatio;
|
|
2271
|
+
/**
|
|
2272
|
+
* Optional clip duration in seconds. Validated server-side against the chosen
|
|
2273
|
+
* provider's allowed durations (e.g. kling 5/10, wan-i2v 5/10/15); omitted →
|
|
2274
|
+
* the model's own default (no behavior change). Mirrors generate-video's
|
|
2275
|
+
* per-model i2v duration lever.
|
|
2276
|
+
*/
|
|
2277
|
+
duration?: number;
|
|
2278
|
+
}
|
|
2279
|
+
interface GenerateObjectMotionResult {
|
|
2280
|
+
jobId: string;
|
|
2281
|
+
}
|
|
2282
|
+
interface ApproveObjectMainImageResult {
|
|
2283
|
+
sourceImageUrl: string;
|
|
2284
|
+
/**
|
|
2285
|
+
* LLM-authored caption. `null` when the LLM caption sub-failed — the wire
|
|
2286
|
+
* sends `""`, normalized to `null` here so consumers see the same
|
|
2287
|
+
* `string | null` semantics as characters. The main image is still set; call
|
|
2288
|
+
* `recaption()` to retry.
|
|
2289
|
+
*/
|
|
2290
|
+
canonicalDescription: string | null;
|
|
2291
|
+
}
|
|
2292
|
+
interface RecaptionObjectResult {
|
|
2293
|
+
canonicalDescription: string;
|
|
2294
|
+
}
|
|
2295
|
+
declare class ObjectsResource {
|
|
2296
|
+
private client;
|
|
2297
|
+
constructor(client: NodaroClient);
|
|
2298
|
+
/**
|
|
2299
|
+
* List the caller's objects. By default returns active objects only;
|
|
2300
|
+
* pass `archived: true` to fetch soft-deleted rows for an "archive" view.
|
|
2301
|
+
* Optional `projectId` scopes the result to a single project.
|
|
2302
|
+
*/
|
|
2303
|
+
list(params?: ListObjectsParams): Promise<{
|
|
2304
|
+
objects: Object$1[];
|
|
2305
|
+
}>;
|
|
2306
|
+
/**
|
|
2307
|
+
* Convenience wrapper for `list({ archived: true })`. Returns soft-deleted
|
|
2308
|
+
* rows so callers can drive a UI "Archived" tab without re-encoding the
|
|
2309
|
+
* query param.
|
|
2310
|
+
*
|
|
2311
|
+
* `archived` is omitted from the param type — it's always set to `true` here.
|
|
2312
|
+
*/
|
|
2313
|
+
listArchived(params?: Omit<ListObjectsParams, "archived">): Promise<{
|
|
2314
|
+
objects: Object$1[];
|
|
2315
|
+
}>;
|
|
2316
|
+
/**
|
|
2317
|
+
* Fetch a single object including in-flight asset job state. Soft-deleted
|
|
2318
|
+
* (archived) rows are NOT returned by id — the route enforces
|
|
2319
|
+
* `deleted_at IS NULL` so archived objects 404 (uniform Pass 10 F-90b
|
|
2320
|
+
* "not_found" — does not leak the deleted vs non-existent distinction).
|
|
2321
|
+
*/
|
|
2322
|
+
get(id: string): Promise<ObjectDetail>;
|
|
2323
|
+
/**
|
|
2324
|
+
* Create a new object. `name` + `nodeId` are required — the route 400s
|
|
2325
|
+
* otherwise. Returns the new row's id.
|
|
2326
|
+
*
|
|
2327
|
+
* Note: the underlying route is the same `POST /v1/objects` upsert that
|
|
2328
|
+
* powers `update()`. This convenience wrapper enforces the INSERT-required
|
|
2329
|
+
* fields at the type level and never sends an `id`.
|
|
2330
|
+
*/
|
|
2331
|
+
create(data: CreateObjectInput): Promise<{
|
|
2332
|
+
id: string;
|
|
2333
|
+
}>;
|
|
2334
|
+
/**
|
|
2335
|
+
* Update an object. Only the fields you pass are written — undefined keys
|
|
2336
|
+
* are NOT touched on the row. Worker-owned asset buckets are intentionally
|
|
2337
|
+
* not exposed on this surface (see `UpdateObjectInput` for the rationale).
|
|
2338
|
+
*
|
|
2339
|
+
* Optimistic-concurrency: pass `expectedUpdatedAt` to require the row's
|
|
2340
|
+
* `updated_at` still matches; on mismatch the route returns 409
|
|
2341
|
+
* `concurrent_modification` carrying the fresh `updatedAt`. The SDK
|
|
2342
|
+
* surfaces that as a generic `NodaroError` with the same code (per Phase
|
|
2343
|
+
* E1 calibration finding — error centralization in `throwApiError`).
|
|
2344
|
+
*/
|
|
2345
|
+
update(id: string, data: UpdateObjectInput): Promise<UpdateObjectResult>;
|
|
2346
|
+
/**
|
|
2347
|
+
* Soft-delete (archive) an object. The row is hidden from `list()` by
|
|
2348
|
+
* default but recoverable via `restore(id)` or visible under
|
|
2349
|
+
* `listArchived()`. Idempotent — repeating a delete on an already-archived
|
|
2350
|
+
* row is a no-op.
|
|
2351
|
+
*/
|
|
2352
|
+
delete(id: string): Promise<{
|
|
2353
|
+
success: true;
|
|
2354
|
+
archived: true;
|
|
2355
|
+
}>;
|
|
2356
|
+
/**
|
|
2357
|
+
* Hard-delete (permanent) an object — the row + every R2 asset it
|
|
2358
|
+
* references. Archived rows ONLY: active objects return 400 `not_archived`.
|
|
2359
|
+
* Call `delete()` first to archive, then `permanentDelete()` to destroy.
|
|
2360
|
+
*
|
|
2361
|
+
* Mirrors the `app_runs` permanent-delete pattern (archive-first) so a
|
|
2362
|
+
* stray SDK / curl caller cannot bypass the studio's archive-first UI
|
|
2363
|
+
* flow.
|
|
2364
|
+
*/
|
|
2365
|
+
permanentDelete(id: string): Promise<{
|
|
2366
|
+
success: true;
|
|
2367
|
+
permanent: true;
|
|
2368
|
+
}>;
|
|
2369
|
+
/**
|
|
2370
|
+
* Un-archive an object. If the original name now collides (case-
|
|
2371
|
+
* insensitive) with an active row, the server auto-suffixes "(restored)"
|
|
2372
|
+
* and returns the effective name.
|
|
2373
|
+
*/
|
|
2374
|
+
restore(id: string): Promise<{
|
|
2375
|
+
id: string;
|
|
2376
|
+
name: string;
|
|
2377
|
+
}>;
|
|
2378
|
+
/**
|
|
2379
|
+
* Fire `POST /v1/generate-object` to produce one or more candidate main
|
|
2380
|
+
* images. With `count > 1`, all jobs are reserved up-front before any
|
|
2381
|
+
* is enqueued — mid-batch failures roll back atomically.
|
|
2382
|
+
*
|
|
2383
|
+
* When `attachToObjectId` is set AND `count === 1`, the worker writes
|
|
2384
|
+
* the result directly to the row's `source_image_url`; otherwise you must
|
|
2385
|
+
* call `approveMainImage()` after picking a candidate.
|
|
2386
|
+
*/
|
|
2387
|
+
generate(data: GenerateObjectInput): Promise<GenerateObjectResult>;
|
|
2388
|
+
/**
|
|
2389
|
+
* Fire `POST /v1/generate-object-asset` to produce a single variant.
|
|
2390
|
+
* When the studio path is set (`attachToObjectId` + `attachToColumn` +
|
|
2391
|
+
* `attachName`), the worker appends `{ name: attachName, url: <result> }`
|
|
2392
|
+
* to the named JSONB array column on completion.
|
|
2393
|
+
*
|
|
2394
|
+
* Note: `attachToColumn` is REQUIRED for `assetType === "custom"` — the
|
|
2395
|
+
* worker can't infer the bucket from the asset type. For canonical asset
|
|
2396
|
+
* types (`angles` / `materials` / `variations` / `motion`), the column is
|
|
2397
|
+
* derived automatically by the route.
|
|
2398
|
+
*/
|
|
2399
|
+
generateAsset(data: GenerateObjectAssetInput): Promise<GenerateObjectAssetResult>;
|
|
2400
|
+
/**
|
|
2401
|
+
* Fire `POST /v1/generate-object-motion` to animate the object's main
|
|
2402
|
+
* image into a motion clip. Image-to-video, single clip per call; the
|
|
2403
|
+
* attach column is hardcoded to `motion_clips` server-side (objects have a
|
|
2404
|
+
* single motion bucket so the caller doesn't supply `attachToColumn`).
|
|
2405
|
+
* When the studio path is set (`attachToObjectId` + `attachName`), the
|
|
2406
|
+
* worker appends `{ name: attachName, url: <result> }` to the row's
|
|
2407
|
+
* `motion_clips` column on completion.
|
|
2408
|
+
*
|
|
2409
|
+
* Defaults: `provider` → `"kling-turbo"`, `aspectRatio` → `"1:1"` (set
|
|
2410
|
+
* server-side via `resolveObjectAspectRatio({ assetType: "motion" })`).
|
|
2411
|
+
*/
|
|
2412
|
+
generateMotion(data: GenerateObjectMotionInput): Promise<GenerateObjectMotionResult>;
|
|
2413
|
+
/**
|
|
2414
|
+
* Approve a completed `generate-object` job as the object's main image.
|
|
2415
|
+
* Sets `source_image_url` and fires the LLM caption (Claude Sonnet vision)
|
|
2416
|
+
* inline. Returns the new main-image URL plus the caption.
|
|
2417
|
+
*
|
|
2418
|
+
* Caption-failure semantics: the route still sends `""` on LLM sub-failure,
|
|
2419
|
+
* but the SDK normalizes `""` → `null` here so `canonicalDescription` carries
|
|
2420
|
+
* the same `string | null` semantics as characters. The main image is still
|
|
2421
|
+
* set; call `recaption()` to retry.
|
|
2422
|
+
*
|
|
2423
|
+
* Optimistic-concurrency: pass `expectedUpdatedAt` to gate the update on
|
|
2424
|
+
* the row's current `updated_at`; on mismatch the route returns 409
|
|
2425
|
+
* `concurrent_modification` carrying the fresh token.
|
|
2426
|
+
*/
|
|
2427
|
+
approveMainImage(id: string, candidateJobId: string, expectedUpdatedAt?: string): Promise<ApproveObjectMainImageResult>;
|
|
2428
|
+
/**
|
|
2429
|
+
* Re-fire the LLM caption against the object's current main image. 502s
|
|
2430
|
+
* on LLM failure (unlike `approveMainImage` which preserves the side-effect
|
|
2431
|
+
* and returns ""); returns 400 `main_image_required` if no main image is
|
|
2432
|
+
* set yet.
|
|
2433
|
+
*
|
|
2434
|
+
* The route is a pure idempotent retry — it does NOT accept an
|
|
2435
|
+
* `expectedUpdatedAt` token (per Phase E1 calibration finding: backend
|
|
2436
|
+
* route is idempotent retry, not gated on optimistic-concurrency).
|
|
2437
|
+
*/
|
|
2438
|
+
recaption(id: string): Promise<RecaptionObjectResult>;
|
|
2439
|
+
}
|
|
2440
|
+
|
|
2441
|
+
/**
|
|
2442
|
+
* Creature voice — IDENTICAL shape + semantics to `Character["voice"]` (the
|
|
2443
|
+
* "talking creature" stack reuses the character voice plumbing verbatim).
|
|
2444
|
+
* Render speech via `client.nodes.run("text-to-speech", { text, voice:
|
|
2445
|
+
* voiceId, provider: ttsProvider, voiceType })`, then feed the audio + the
|
|
2446
|
+
* creature's `sourceImageUrl` to `client.nodes.run("lip-sync", …)` (or
|
|
2447
|
+
* speech-to-video) for a talking-creature clip.
|
|
2448
|
+
*/
|
|
2449
|
+
interface CreatureVoice {
|
|
2450
|
+
voiceId: string;
|
|
2451
|
+
voiceName: string;
|
|
2452
|
+
traits: string;
|
|
2453
|
+
voiceType?: "premade" | "library" | "custom";
|
|
2454
|
+
/** Playable preview sample (client-played only). */
|
|
2455
|
+
previewUrl?: string;
|
|
2456
|
+
/** Recommended TTS provider — send as `provider` on text-to-speech. */
|
|
2457
|
+
ttsProvider?: TtsProvider;
|
|
2458
|
+
}
|
|
2459
|
+
/**
|
|
2460
|
+
* Re-export the shared `CreatureAttachColumn` union + its runtime tuple so SDK
|
|
2461
|
+
* consumers don't have to add `@nodaro/shared` as a second dependency just to
|
|
2462
|
+
* typecheck the `attachToColumn` field. Single source of truth lives in
|
|
2463
|
+
* `@nodaro/shared/entity-prompts` (`CREATURE_ATTACH_COLUMNS`).
|
|
2464
|
+
*
|
|
2465
|
+
* `CreatureAspectRatio` is re-exported as an alias of the shared
|
|
2466
|
+
* `ObjectAspectRatio` — `generateMotion`'s `aspectRatio` field is the 5-value
|
|
2467
|
+
* object enum (1:1 / 3:4 / 16:9 / 9:16 / 4:3). The creature motion route
|
|
2468
|
+
* deliberately REUSES `OBJECT_ASPECT_OPTIONS` server-side (a creature reference
|
|
2469
|
+
* clip is centered product-showcase framing, not cinematic 16:9), so the SDK
|
|
2470
|
+
* surfaces the same enum under a creature-friendly name. The runtime tuple is
|
|
2471
|
+
* re-exported as `CREATURE_ASPECT_OPTIONS` / `CREATURE_ASPECT_DEFAULTS` aliases.
|
|
2472
|
+
*/
|
|
2473
|
+
/**
|
|
2474
|
+
* Creature asset-type enum — the kinds of variant a user can generate off a
|
|
2475
|
+
* creature's anchor main image. Mirrors the literal accepted by
|
|
2476
|
+
* `POST /v1/generate-creature-asset` (`backend/src/routes/generate-creature-asset.ts`).
|
|
2477
|
+
*
|
|
2478
|
+
* Delta vs `ObjectAssetType`: object's `materials` becomes `poses` (a creature
|
|
2479
|
+
* has poses, not materials), and there is NO `motion` value — creature motion
|
|
2480
|
+
* variants flow through the dedicated `/v1/generate-creature-motion` endpoint
|
|
2481
|
+
* (worker-side a different BullMQ job type). `custom` is the free-form bucket;
|
|
2482
|
+
* callers must supply `attachToColumn` explicitly since the worker can't infer
|
|
2483
|
+
* the destination from the asset type.
|
|
2484
|
+
*
|
|
2485
|
+
* The shared `@nodaro/shared` package does NOT export a `CREATURE_ASSET_TYPES`
|
|
2486
|
+
* tuple (the route validates an inline Zod enum), so the SDK defines its own
|
|
2487
|
+
* single-source-of-truth tuple here.
|
|
2488
|
+
*/
|
|
2489
|
+
declare const CREATURE_ASSET_TYPES: readonly ["angles", "poses", "variations", "custom"];
|
|
2490
|
+
type CreatureAssetType = (typeof CREATURE_ASSET_TYPES)[number];
|
|
2491
|
+
/**
|
|
2492
|
+
* Reference-photo kind discriminator — the mood-board roles a user can attach
|
|
2493
|
+
* to a creature. Mirrors the `reference_photos.kind` field accepted by
|
|
2494
|
+
* `backend/src/routes/creatures.ts` (the route accepts open strings; this SDK
|
|
2495
|
+
* type narrows to the 6 canonical roles surfaced by the Studio). `other` is
|
|
2496
|
+
* the free-form bucket.
|
|
2497
|
+
*/
|
|
2498
|
+
type CreatureReferencePhotoKind = "front" | "side" | "detail" | "context" | "moodBoard" | "other";
|
|
2499
|
+
interface CreatureReferencePhoto {
|
|
2500
|
+
url: string;
|
|
2501
|
+
kind: CreatureReferencePhotoKind;
|
|
2502
|
+
}
|
|
2503
|
+
/**
|
|
2504
|
+
* A creature record returned by Nodaro's REST API. Mirrors the camelCase
|
|
2505
|
+
* shape produced by `backend/src/routes/creatures.ts::toCamel()`.
|
|
2506
|
+
*
|
|
2507
|
+
* Asset buckets (`angles`, `poses`, `variations`, `motionClips`) are
|
|
2508
|
+
* independent JSONB arrays keyed by a human-readable variant name (e.g.
|
|
2509
|
+
* `"front"`, `"walking"`, `"scarred"`). Each entry's `url` points at an
|
|
2510
|
+
* R2-hosted asset.
|
|
2511
|
+
*
|
|
2512
|
+
* Creature delta vs object:
|
|
2513
|
+
* - `species` — free-text creature type (e.g. `"dragon"`, `"wolf"`). This is
|
|
2514
|
+
* the subject of the establishing-shot prompt and the primary creature
|
|
2515
|
+
* differentiator (object has no equivalent).
|
|
2516
|
+
* - `poses` (where object has `materials`) — the pose-variant asset bucket.
|
|
2517
|
+
* - `category` is free-text (NOT object's fixed 10-value enum) — a creature
|
|
2518
|
+
* can be any animal/type.
|
|
2519
|
+
*
|
|
2520
|
+
* Identity-foundation fields:
|
|
2521
|
+
* - `referencePhotos` — caller-supplied mood-board refs (cap 20).
|
|
2522
|
+
* - `canonicalDescription` — ~80–120-word LLM-authored visual caption,
|
|
2523
|
+
* populated by `approveMainImage()` / `recaption()`. The wire still sends
|
|
2524
|
+
* `""` on caption sub-failure, but `get()` normalizes `""` → `null` so
|
|
2525
|
+
* consumers see the same `string | null` semantics as characters.
|
|
2526
|
+
* - `styleLock` — whether asset gens should anchor to the canonical style
|
|
2527
|
+
* captured at approval time. Defaults to `true` on new rows.
|
|
2528
|
+
*/
|
|
2529
|
+
interface Creature {
|
|
2530
|
+
id: string;
|
|
2531
|
+
userId: string;
|
|
2532
|
+
nodeId: string;
|
|
2533
|
+
projectId: string | null;
|
|
2534
|
+
name: string;
|
|
2535
|
+
description: string | null;
|
|
2536
|
+
/** Free-text creature type/species (e.g. "dragon", "wolf") — the creature
|
|
2537
|
+
* delta vs object. `null` when unset. */
|
|
2538
|
+
species: string | null;
|
|
2539
|
+
category: string | null;
|
|
2540
|
+
style: string | null;
|
|
2541
|
+
sourceImageUrl: string | null;
|
|
2542
|
+
/** MODEL_CATALOG image-model id the main image was generated with (or `null`).
|
|
2543
|
+
* Set on create + editable via the update route. */
|
|
2544
|
+
imageProvider: string | null;
|
|
2545
|
+
angles: Array<{
|
|
2546
|
+
name: string;
|
|
2547
|
+
url: string;
|
|
2548
|
+
}>;
|
|
2549
|
+
poses: Array<{
|
|
2550
|
+
name: string;
|
|
2551
|
+
url: string;
|
|
2552
|
+
}>;
|
|
2553
|
+
variations: Array<{
|
|
2554
|
+
name: string;
|
|
2555
|
+
url: string;
|
|
2556
|
+
}>;
|
|
2557
|
+
motionClips: Array<{
|
|
2558
|
+
name: string;
|
|
2559
|
+
url: string;
|
|
2560
|
+
}>;
|
|
2561
|
+
/** Named Creature Boards — dense reference sheets, one per variant/mood
|
|
2562
|
+
* (the `generate-image/creature-board` factory preset rendered from the
|
|
2563
|
+
* creature's images). A first-class bucket: community publish snapshots it
|
|
2564
|
+
* and clone hands the consumer their own copy to extend. Defaults to `[]`.
|
|
2565
|
+
* `type` marks an image-collage `"identity"` sheet vs a plain `"looks"`
|
|
2566
|
+
* board; `sourceImages` are the R2 URLs it was collaged from. Both optional
|
|
2567
|
+
* + backward-compatible (legacy boards have neither). */
|
|
2568
|
+
boards?: Array<{
|
|
2569
|
+
name: string;
|
|
2570
|
+
url: string;
|
|
2571
|
+
type?: "looks" | "identity";
|
|
2572
|
+
sourceImages?: string[];
|
|
2573
|
+
}> | null;
|
|
2574
|
+
/** The creature's voice (the "talking creature" stack) — same shape and
|
|
2575
|
+
* flow as `Character["voice"]`. `null` when no voice is selected. */
|
|
2576
|
+
voice?: CreatureVoice | null;
|
|
2577
|
+
referencePhotos: CreatureReferencePhoto[];
|
|
2578
|
+
/** `null` when no caption is set (or the LLM caption sub-failed) — the wire
|
|
2579
|
+
* sends `""`, normalized to `null` in `get()` to match character semantics. */
|
|
2580
|
+
canonicalDescription: string | null;
|
|
2581
|
+
styleLock: boolean;
|
|
2582
|
+
/** The user's chosen DEFAULT asset take per variant (Studio version history).
|
|
2583
|
+
* OPAQUE map: key `"<bucket>:<variant>"` (e.g. `"angles:front"`) → the chosen
|
|
2584
|
+
* asset URL (one already present in that bucket). Stored verbatim — keys are
|
|
2585
|
+
* NOT normalized; soft-capped server-side at 200 keys / 2048-char values
|
|
2586
|
+
* (overflow dropped silently). Defaults to `{}`. */
|
|
2587
|
+
selectedAssetByVariant?: Record<string, string> | null;
|
|
2588
|
+
deletedAt: string | null;
|
|
2589
|
+
createdAt: string;
|
|
2590
|
+
updatedAt: string;
|
|
2591
|
+
}
|
|
2592
|
+
/**
|
|
2593
|
+
* GET /v1/creatures/:id may append a `pendingJobs` bucket the studio uses to
|
|
2594
|
+
* rehydrate spinners after a reload. Optional on the SDK surface — it doesn't
|
|
2595
|
+
* appear on `list()` rows.
|
|
2596
|
+
*/
|
|
2597
|
+
interface CreatureDetail extends Creature {
|
|
2598
|
+
pendingJobs?: Array<{
|
|
2599
|
+
jobId: string;
|
|
2600
|
+
assetType: string;
|
|
2601
|
+
name: string;
|
|
2602
|
+
status: string;
|
|
2603
|
+
}>;
|
|
2604
|
+
}
|
|
2605
|
+
/**
|
|
2606
|
+
* Body for `client.creatures.create()`. Mirrors the INSERT branch of
|
|
2607
|
+
* `upsertCreatureBody` in `backend/src/routes/creatures.ts`. `name` + `nodeId`
|
|
2608
|
+
* are required on create.
|
|
2609
|
+
*/
|
|
2610
|
+
interface CreateCreatureInput {
|
|
2611
|
+
nodeId: string;
|
|
2612
|
+
name: string;
|
|
2613
|
+
description?: string;
|
|
2614
|
+
/** Free-text creature type/species (the creature delta vs object). */
|
|
2615
|
+
species?: string;
|
|
2616
|
+
/** Free-text category (NOT object's fixed enum — a creature can be anything). */
|
|
2617
|
+
category?: string;
|
|
2618
|
+
style?: string;
|
|
2619
|
+
workflowId?: string;
|
|
2620
|
+
projectId?: string;
|
|
2621
|
+
sourceImageUrl?: string;
|
|
2622
|
+
/** Persistent image-model id (a MODEL_CATALOG image model). Validated
|
|
2623
|
+
* server-side — unknown / non-image / "" is stored as `null`. */
|
|
2624
|
+
imageProvider?: string | null;
|
|
2625
|
+
referencePhotos?: CreatureReferencePhoto[];
|
|
2626
|
+
canonicalDescription?: string;
|
|
2627
|
+
styleLock?: boolean;
|
|
2628
|
+
/** The user's chosen DEFAULT asset take per variant. OPAQUE
|
|
2629
|
+
* `"<bucket>:<variant>"` → chosen-URL map; a write REPLACES the whole map.
|
|
2630
|
+
* Keys stored verbatim; soft-capped server-side at 200 keys / 2048-char values. */
|
|
2631
|
+
selectedAssetByVariant?: Record<string, string>;
|
|
2632
|
+
/** Initial voice selection (see `CreatureVoice`). Omit for a voiceless creature. */
|
|
2633
|
+
voice?: CreatureVoice | null;
|
|
2634
|
+
}
|
|
2635
|
+
/**
|
|
2636
|
+
* Body for `client.creatures.update()`. Mirrors the UPDATE branch of
|
|
2637
|
+
* `upsertCreatureBody` in `backend/src/routes/creatures.ts`.
|
|
2638
|
+
*
|
|
2639
|
+
* Worker-owned asset buckets (`angles` / `poses` / `variations` /
|
|
2640
|
+
* `motionClips`) are deliberately omitted — the route drops them on UPDATE
|
|
2641
|
+
* so a Studio auto-save with a stale snapshot cannot clobber the worker's
|
|
2642
|
+
* atomic `append_creature_asset()` writes.
|
|
2643
|
+
*
|
|
2644
|
+
* `expectedUpdatedAt` is the optimistic-concurrency token: when present, the
|
|
2645
|
+
* UPDATE only succeeds if the row's `updated_at` still matches; on mismatch
|
|
2646
|
+
* the route returns 409 so the studio can re-fetch + merge.
|
|
2647
|
+
*/
|
|
2648
|
+
interface UpdateCreatureInput {
|
|
2649
|
+
name?: string;
|
|
2650
|
+
description?: string;
|
|
2651
|
+
/** Free-text creature type/species (the creature delta vs object). */
|
|
2652
|
+
species?: string;
|
|
2653
|
+
category?: string;
|
|
2654
|
+
style?: string;
|
|
2655
|
+
sourceImageUrl?: string;
|
|
2656
|
+
/** Persistent image-model id (a MODEL_CATALOG image model). Validated
|
|
2657
|
+
* server-side — unknown / non-image / "" is stored as `null`. */
|
|
2658
|
+
imageProvider?: string | null;
|
|
2659
|
+
referencePhotos?: CreatureReferencePhoto[];
|
|
2660
|
+
canonicalDescription?: string;
|
|
2661
|
+
styleLock?: boolean;
|
|
2662
|
+
/** The user's chosen DEFAULT asset take per variant. OPAQUE
|
|
2663
|
+
* `"<bucket>:<variant>"` → chosen-URL map; a write REPLACES the whole map
|
|
2664
|
+
* (omit to leave untouched). Keys stored verbatim; soft-capped server-side
|
|
2665
|
+
* at 200 keys / 2048-char values. */
|
|
2666
|
+
selectedAssetByVariant?: Record<string, string>;
|
|
2667
|
+
/** Named Creature Boards (see `Creature.boards`) — whole-array replace,
|
|
2668
|
+
* USER-owned (unlike the worker-owned buckets it flows through UPDATE).
|
|
2669
|
+
* Server caps: 24 boards, 200-char names, 30 sourceImages per board. */
|
|
2670
|
+
boards?: Array<{
|
|
2671
|
+
name: string;
|
|
2672
|
+
url: string;
|
|
2673
|
+
type?: "looks" | "identity";
|
|
2674
|
+
sourceImages?: string[];
|
|
2675
|
+
}>;
|
|
2676
|
+
/** Voice selection (see `CreatureVoice`) — whole-object replace; pass
|
|
2677
|
+
* `null` to clear the voice, omit to leave untouched. */
|
|
2678
|
+
voice?: CreatureVoice | null;
|
|
2679
|
+
expectedUpdatedAt?: string;
|
|
2680
|
+
}
|
|
2681
|
+
interface UpdateCreatureResult {
|
|
2682
|
+
id: string;
|
|
2683
|
+
updatedAt: string;
|
|
2684
|
+
}
|
|
2685
|
+
/**
|
|
2686
|
+
* Combined create + update body (parameter for both branches). Exported for
|
|
2687
|
+
* callers that want to drive a single `upsert` flow without picking between
|
|
2688
|
+
* `Create*` and `Update*`. Mirrors the `upsertCreatureBody` Zod schema in
|
|
2689
|
+
* `backend/src/routes/creatures.ts`. `nodeId` + `name` are required on INSERT;
|
|
2690
|
+
* `id` flips the route into UPDATE mode.
|
|
2691
|
+
*/
|
|
2692
|
+
interface UpsertCreatureInput extends CreateCreatureInput {
|
|
2693
|
+
id?: string;
|
|
2694
|
+
expectedUpdatedAt?: string;
|
|
2695
|
+
}
|
|
2696
|
+
type UpsertCreatureResult = {
|
|
2697
|
+
id: string;
|
|
2698
|
+
} | UpdateCreatureResult;
|
|
2699
|
+
interface ListCreaturesParams {
|
|
2700
|
+
/** When true, return archived creatures instead of active ones. */
|
|
2701
|
+
archived?: boolean;
|
|
2702
|
+
/** Optional project filter — server-scoped to the caller's user. */
|
|
2703
|
+
projectId?: string;
|
|
2704
|
+
}
|
|
2705
|
+
/**
|
|
2706
|
+
* Input for `client.creatures.generate()` — fires the
|
|
2707
|
+
* `POST /v1/generate-creature` route. Produces 1–10 candidate
|
|
2708
|
+
* main images; each lands as one `jobs` row in `pending` state and is then
|
|
2709
|
+
* enqueued for the worker.
|
|
2710
|
+
*
|
|
2711
|
+
* When `attachToCreatureId` is set AND `count === 1`, the worker writes the
|
|
2712
|
+
* resulting URL directly to `creatures.source_image_url` on completion —
|
|
2713
|
+
* caller doesn't need a separate `approveMainImage` call. Multi-candidate
|
|
2714
|
+
* batches MUST go through explicit approval so the user picks the winner.
|
|
2715
|
+
*
|
|
2716
|
+
* `seedPromptHint` flows the parameter-picker's prompt fragment through to the
|
|
2717
|
+
* worker so a catalog selection (e.g. "armored frost dragon") gets appended to
|
|
2718
|
+
* the generated prompt context.
|
|
2719
|
+
*/
|
|
2720
|
+
interface GenerateCreatureInput {
|
|
2721
|
+
name: string;
|
|
2722
|
+
description?: string;
|
|
2723
|
+
userPrompt?: string;
|
|
2724
|
+
/** Free-text creature type/species (the creature delta vs object). */
|
|
2725
|
+
species?: string;
|
|
2726
|
+
category?: string;
|
|
2727
|
+
style?: "realistic" | "anime" | "3d-pixar" | "illustration";
|
|
2728
|
+
sourceImageUrl?: string;
|
|
2729
|
+
provider?: string;
|
|
2730
|
+
/** Number of candidate images to generate (1–10; server-validated). */
|
|
2731
|
+
count?: number;
|
|
2732
|
+
/** Auto-attach the result to this creature row (single-candidate only). */
|
|
2733
|
+
attachToCreatureId?: string;
|
|
2734
|
+
/** Parameter-picker prompt-fragment pass-through. */
|
|
2735
|
+
seedPromptHint?: string;
|
|
2736
|
+
/** Optional name to set on the attached row alongside the main image. */
|
|
2737
|
+
attachName?: string;
|
|
2738
|
+
/** Optimistic-concurrency token for the single-candidate auto-attach path. */
|
|
2739
|
+
expectedUpdatedAt?: string;
|
|
2740
|
+
}
|
|
2741
|
+
/**
|
|
2742
|
+
* `generate()` response — `jobIds` is ALWAYS present (the harmonized contract,
|
|
2743
|
+
* matching characters). `jobId` is a deprecated back-compat alias populated only
|
|
2744
|
+
* on `count === 1`; prefer `jobIds`. (Will be removed on the next major.)
|
|
2745
|
+
*/
|
|
2746
|
+
interface GenerateCreatureResult {
|
|
2747
|
+
jobIds: string[];
|
|
2748
|
+
/** @deprecated count===1 back-compat alias — use `jobIds`. */
|
|
2749
|
+
jobId?: string;
|
|
2750
|
+
}
|
|
2751
|
+
/**
|
|
2752
|
+
* Input for `client.creatures.generateAsset()` — fires the
|
|
2753
|
+
* `POST /v1/generate-creature-asset` route. Produces a single
|
|
2754
|
+
* angles / poses / variations / custom variant.
|
|
2755
|
+
*
|
|
2756
|
+
* When all three studio-path fields are set (`attachToCreatureId` +
|
|
2757
|
+
* `attachToColumn` + `attachName`), the worker appends
|
|
2758
|
+
* `{ name: attachName, url: <result> }` to the named JSONB array column on
|
|
2759
|
+
* the user's creature row on completion. `attachToColumn` is REQUIRED for
|
|
2760
|
+
* `assetType === "custom"` — the worker can't infer the bucket from the
|
|
2761
|
+
* asset type.
|
|
2762
|
+
*/
|
|
2763
|
+
interface GenerateCreatureAssetInput {
|
|
2764
|
+
assetType: CreatureAssetType;
|
|
2765
|
+
variant: string;
|
|
2766
|
+
name: string;
|
|
2767
|
+
description?: string;
|
|
2768
|
+
userPrompt?: string;
|
|
2769
|
+
category?: string;
|
|
2770
|
+
style?: string;
|
|
2771
|
+
sourceImageUrl?: string;
|
|
2772
|
+
provider?: string;
|
|
2773
|
+
attachToCreatureId?: string;
|
|
2774
|
+
attachToColumn?: CreatureAttachColumn;
|
|
2775
|
+
attachName?: string;
|
|
2776
|
+
/** Parameter-picker prompt-fragment pass-through. */
|
|
2777
|
+
seedPromptHint?: string;
|
|
2778
|
+
}
|
|
2779
|
+
interface GenerateCreatureAssetResult {
|
|
2780
|
+
jobId: string;
|
|
2781
|
+
}
|
|
2782
|
+
/**
|
|
2783
|
+
* Input for `client.creatures.generateMotion()` — fires the
|
|
2784
|
+
* `POST /v1/generate-creature-motion` route. Produces a single motion clip
|
|
2785
|
+
* (idle, prowl, attack, etc.) animated FROM a static creature-shot image.
|
|
2786
|
+
*
|
|
2787
|
+
* Mirrors `client.objects.generateMotion()` — the creature motion route reuses
|
|
2788
|
+
* the entity-agnostic object motion helpers server-side. The route hardcodes
|
|
2789
|
+
* the attach column to `motion_clips` — callers supply `attachToCreatureId` +
|
|
2790
|
+
* `attachName` only.
|
|
2791
|
+
*
|
|
2792
|
+
* `sourceImageUrl` is REQUIRED — image-to-video needs a source frame and the
|
|
2793
|
+
* route has no fallback (the studio path supplies the canonical creature-shot
|
|
2794
|
+
* URL explicitly).
|
|
2795
|
+
*
|
|
2796
|
+
* Defaults vs location:
|
|
2797
|
+
* - `provider` defaults to `"kling-turbo"` (not location's `"kling"`)
|
|
2798
|
+
* - `aspectRatio` defaults to `"1:1"` server-side via
|
|
2799
|
+
* `resolveObjectAspectRatio({ assetType: "motion" })` — creatures use
|
|
2800
|
+
* centered reference framing, not cinematic establishing shots.
|
|
2801
|
+
*
|
|
2802
|
+
* When the studio path is set (`attachToCreatureId` + `attachName`), the
|
|
2803
|
+
* worker appends `{ name: attachName, url: <result> }` to the creature row's
|
|
2804
|
+
* `motion_clips` JSONB column on completion.
|
|
2805
|
+
*/
|
|
2806
|
+
interface GenerateCreatureMotionInput {
|
|
2807
|
+
motionPrompt: string;
|
|
2808
|
+
sourceImageUrl: string;
|
|
2809
|
+
provider?: string;
|
|
2810
|
+
name: string;
|
|
2811
|
+
/** Source clip URL — when set, worker routes to video-to-video refine. */
|
|
2812
|
+
refineFromVideoUrl?: string;
|
|
2813
|
+
category?: string;
|
|
2814
|
+
style?: "realistic" | "anime" | "3d-pixar" | "illustration";
|
|
2815
|
+
canonicalDescription?: string;
|
|
2816
|
+
/** Parameter-picker prompt-fragment pass-through. */
|
|
2817
|
+
seedPromptHint?: string;
|
|
2818
|
+
attachToCreatureId?: string;
|
|
2819
|
+
attachName?: string;
|
|
2820
|
+
/**
|
|
2821
|
+
* Optional aspect ratio override. Defaults to 1:1 server-side. One of the
|
|
2822
|
+
* 5-value `CreatureAspectRatio` union (1:1 / 3:4 / 16:9 / 9:16 / 4:3) — the
|
|
2823
|
+
* creature route reuses the object aspect enum.
|
|
2824
|
+
*/
|
|
2825
|
+
aspectRatio?: ObjectAspectRatio;
|
|
2826
|
+
/**
|
|
2827
|
+
* Optional clip duration in seconds. Validated server-side against the chosen
|
|
2828
|
+
* provider's allowed durations (e.g. kling 5/10, wan-i2v 5/10/15); omitted →
|
|
2829
|
+
* the model's own default (no behavior change). Mirrors generate-video's
|
|
2830
|
+
* per-model i2v duration lever.
|
|
2831
|
+
*/
|
|
2832
|
+
duration?: number;
|
|
2833
|
+
}
|
|
2834
|
+
interface GenerateCreatureMotionResult {
|
|
2835
|
+
jobId: string;
|
|
2836
|
+
}
|
|
2837
|
+
interface ApproveCreatureMainImageResult {
|
|
2838
|
+
sourceImageUrl: string;
|
|
2839
|
+
/**
|
|
2840
|
+
* LLM-authored caption. `null` when the LLM caption sub-failed — the wire
|
|
2841
|
+
* sends `""`, normalized to `null` here so consumers see the same
|
|
2842
|
+
* `string | null` semantics as characters. The main image is still set; call
|
|
2843
|
+
* `recaption()` to retry.
|
|
2844
|
+
*/
|
|
2845
|
+
canonicalDescription: string | null;
|
|
2846
|
+
}
|
|
2847
|
+
interface RecaptionCreatureResult {
|
|
2848
|
+
canonicalDescription: string;
|
|
2849
|
+
}
|
|
2850
|
+
declare class CreaturesResource {
|
|
2851
|
+
private client;
|
|
2852
|
+
constructor(client: NodaroClient);
|
|
2853
|
+
/**
|
|
2854
|
+
* List the caller's creatures. By default returns active creatures only;
|
|
2855
|
+
* pass `archived: true` to fetch soft-deleted rows for an "archive" view.
|
|
2856
|
+
* Optional `projectId` scopes the result to a single project.
|
|
2857
|
+
*/
|
|
2858
|
+
list(params?: ListCreaturesParams): Promise<{
|
|
2859
|
+
creatures: Creature[];
|
|
2860
|
+
}>;
|
|
2861
|
+
/**
|
|
2862
|
+
* Convenience wrapper for `list({ archived: true })`. Returns soft-deleted
|
|
2863
|
+
* rows so callers can drive a UI "Archived" tab without re-encoding the
|
|
2864
|
+
* query param.
|
|
2865
|
+
*
|
|
2866
|
+
* `archived` is omitted from the param type — it's always set to `true` here.
|
|
2867
|
+
*/
|
|
2868
|
+
listArchived(params?: Omit<ListCreaturesParams, "archived">): Promise<{
|
|
2869
|
+
creatures: Creature[];
|
|
2870
|
+
}>;
|
|
2871
|
+
/**
|
|
2872
|
+
* Fetch a single creature including in-flight asset job state. Soft-deleted
|
|
2873
|
+
* (archived) rows are NOT returned by id — the route enforces
|
|
2874
|
+
* `deleted_at IS NULL` so archived creatures 404 (uniform "not_found" — does
|
|
2875
|
+
* not leak the deleted vs non-existent distinction).
|
|
2876
|
+
*/
|
|
2877
|
+
get(id: string): Promise<CreatureDetail>;
|
|
2878
|
+
/**
|
|
2879
|
+
* Create a new creature. `name` + `nodeId` are required — the route 400s
|
|
2880
|
+
* otherwise. Returns the new row's id.
|
|
2881
|
+
*
|
|
2882
|
+
* Note: the underlying route is the same `POST /v1/creatures` upsert that
|
|
2883
|
+
* powers `update()`. This convenience wrapper enforces the INSERT-required
|
|
2884
|
+
* fields at the type level and never sends an `id`.
|
|
2885
|
+
*/
|
|
2886
|
+
create(data: CreateCreatureInput): Promise<{
|
|
2887
|
+
id: string;
|
|
2888
|
+
}>;
|
|
2889
|
+
/**
|
|
2890
|
+
* Update a creature. Only the fields you pass are written — undefined keys
|
|
2891
|
+
* are NOT touched on the row. Worker-owned asset buckets are intentionally
|
|
2892
|
+
* not exposed on this surface (see `UpdateCreatureInput` for the rationale).
|
|
2893
|
+
*
|
|
2894
|
+
* Optimistic-concurrency: pass `expectedUpdatedAt` to require the row's
|
|
2895
|
+
* `updated_at` still matches; on mismatch the route returns 409
|
|
2896
|
+
* `concurrent_modification` carrying the fresh `updatedAt`. The SDK
|
|
2897
|
+
* surfaces that as a generic `NodaroError` with the same code.
|
|
2898
|
+
*/
|
|
2899
|
+
update(id: string, data: UpdateCreatureInput): Promise<UpdateCreatureResult>;
|
|
2900
|
+
/**
|
|
2901
|
+
* Soft-delete (archive) a creature. The row is hidden from `list()` by
|
|
2902
|
+
* default but recoverable via `restore(id)` or visible under
|
|
2903
|
+
* `listArchived()`. Idempotent — repeating a delete on an already-archived
|
|
2904
|
+
* row is a no-op.
|
|
2905
|
+
*/
|
|
2906
|
+
delete(id: string): Promise<{
|
|
2907
|
+
success: true;
|
|
2908
|
+
archived: true;
|
|
2909
|
+
}>;
|
|
2910
|
+
/**
|
|
2911
|
+
* Hard-delete (permanent) a creature — the row + every R2 asset it
|
|
2912
|
+
* references. Archived rows ONLY: active creatures return 400 `not_archived`.
|
|
2913
|
+
* Call `delete()` first to archive, then `permanentDelete()` to destroy.
|
|
2914
|
+
*
|
|
2915
|
+
* Mirrors the `app_runs` permanent-delete pattern (archive-first) so a
|
|
2916
|
+
* stray SDK / curl caller cannot bypass the studio's archive-first UI
|
|
2917
|
+
* flow.
|
|
2918
|
+
*/
|
|
2919
|
+
permanentDelete(id: string): Promise<{
|
|
2920
|
+
success: true;
|
|
2921
|
+
permanent: true;
|
|
2922
|
+
}>;
|
|
2923
|
+
/**
|
|
2924
|
+
* Un-archive a creature. If the original name now collides (case-
|
|
2925
|
+
* insensitive) with an active row, the server auto-suffixes "(restored)"
|
|
2926
|
+
* and returns the effective name.
|
|
2927
|
+
*/
|
|
2928
|
+
restore(id: string): Promise<{
|
|
2929
|
+
id: string;
|
|
2930
|
+
name: string;
|
|
2931
|
+
}>;
|
|
2932
|
+
/**
|
|
2933
|
+
* Fire `POST /v1/generate-creature` to produce one or more candidate main
|
|
2934
|
+
* images. With `count > 1`, all jobs are reserved up-front before any
|
|
2935
|
+
* is enqueued — mid-batch failures roll back atomically.
|
|
2936
|
+
*
|
|
2937
|
+
* When `attachToCreatureId` is set AND `count === 1`, the worker writes
|
|
2938
|
+
* the result directly to the row's `source_image_url`; otherwise you must
|
|
2939
|
+
* call `approveMainImage()` after picking a candidate.
|
|
2940
|
+
*/
|
|
2941
|
+
generate(data: GenerateCreatureInput): Promise<GenerateCreatureResult>;
|
|
2942
|
+
/**
|
|
2943
|
+
* Fire `POST /v1/generate-creature-asset` to produce a single variant.
|
|
2944
|
+
* When the studio path is set (`attachToCreatureId` + `attachToColumn` +
|
|
2945
|
+
* `attachName`), the worker appends `{ name: attachName, url: <result> }`
|
|
2946
|
+
* to the named JSONB array column on completion.
|
|
2947
|
+
*
|
|
2948
|
+
* Note: `attachToColumn` is REQUIRED for `assetType === "custom"` — the
|
|
2949
|
+
* worker can't infer the bucket from the asset type. For canonical asset
|
|
2950
|
+
* types (`angles` / `poses` / `variations`), the column is derived
|
|
2951
|
+
* automatically by the route.
|
|
2952
|
+
*/
|
|
2953
|
+
generateAsset(data: GenerateCreatureAssetInput): Promise<GenerateCreatureAssetResult>;
|
|
2954
|
+
/**
|
|
2955
|
+
* Fire `POST /v1/generate-creature-motion` to animate the creature's main
|
|
2956
|
+
* image into a motion clip. Image-to-video, single clip per call; the
|
|
2957
|
+
* attach column is hardcoded to `motion_clips` server-side (creatures have a
|
|
2958
|
+
* single motion bucket so the caller doesn't supply `attachToColumn`).
|
|
2959
|
+
* When the studio path is set (`attachToCreatureId` + `attachName`), the
|
|
2960
|
+
* worker appends `{ name: attachName, url: <result> }` to the row's
|
|
2961
|
+
* `motion_clips` column on completion.
|
|
2962
|
+
*
|
|
2963
|
+
* Defaults: `provider` → `"kling-turbo"`, `aspectRatio` → `"1:1"` (set
|
|
2964
|
+
* server-side via `resolveObjectAspectRatio({ assetType: "motion" })`).
|
|
2965
|
+
*/
|
|
2966
|
+
generateMotion(data: GenerateCreatureMotionInput): Promise<GenerateCreatureMotionResult>;
|
|
2967
|
+
/**
|
|
2968
|
+
* Approve a completed `generate-creature` job as the creature's main image.
|
|
2969
|
+
* Sets `source_image_url` and fires the LLM caption (Claude Sonnet vision)
|
|
2970
|
+
* inline. Returns the new main-image URL plus the caption.
|
|
2971
|
+
*
|
|
2972
|
+
* Caption-failure semantics: the route still sends `""` on LLM sub-failure,
|
|
2973
|
+
* but the SDK normalizes `""` → `null` here so `canonicalDescription` carries
|
|
2974
|
+
* the same `string | null` semantics as characters. The main image is still
|
|
2975
|
+
* set; call `recaption()` to retry.
|
|
2976
|
+
*
|
|
2977
|
+
* Optimistic-concurrency: pass `expectedUpdatedAt` to gate the update on
|
|
2978
|
+
* the row's current `updated_at`; on mismatch the route returns 409
|
|
2979
|
+
* `concurrent_modification` carrying the fresh token.
|
|
2980
|
+
*/
|
|
2981
|
+
approveMainImage(id: string, candidateJobId: string, expectedUpdatedAt?: string): Promise<ApproveCreatureMainImageResult>;
|
|
2982
|
+
/**
|
|
2983
|
+
* Re-fire the LLM caption against the creature's current main image. 502s
|
|
2984
|
+
* on LLM failure (unlike `approveMainImage` which preserves the side-effect
|
|
2985
|
+
* and returns ""); returns 400 `main_image_required` if no main image is
|
|
2986
|
+
* set yet.
|
|
2987
|
+
*
|
|
2988
|
+
* The route is a pure idempotent retry — it does NOT accept an
|
|
2989
|
+
* `expectedUpdatedAt` token (backend route is idempotent retry, not gated on
|
|
2990
|
+
* optimistic-concurrency).
|
|
2991
|
+
*/
|
|
2992
|
+
recaption(id: string): Promise<RecaptionCreatureResult>;
|
|
2993
|
+
}
|
|
2994
|
+
|
|
2995
|
+
/**
|
|
2996
|
+
* Owner-scoped pipeline record returned by `get` / `list` (the server strips
|
|
2997
|
+
* `user_id`). Mirrors the public field set of `GET /v1/pipelines/:id`.
|
|
2998
|
+
*/
|
|
2999
|
+
interface PipelineRecord {
|
|
3000
|
+
id: string;
|
|
3001
|
+
status: PipelineStatus;
|
|
3002
|
+
current_stage: string | null;
|
|
3003
|
+
spent_credits: number;
|
|
3004
|
+
reserved_credits: number;
|
|
3005
|
+
upfront_credit_estimate: number;
|
|
3006
|
+
branched_from_pipeline_id: string | null;
|
|
3007
|
+
branched_from_stage: string | null;
|
|
3008
|
+
mode: PipelineMode | null;
|
|
3009
|
+
failure_reason: string | null;
|
|
3010
|
+
current_progress_message: string | null;
|
|
3011
|
+
}
|
|
3012
|
+
/** One stage currently awaiting approval (from `pendingApprovals`). */
|
|
3013
|
+
interface PendingApproval {
|
|
3014
|
+
stage_name: PipelineStageName;
|
|
3015
|
+
/** Stage output snapshot; shape varies by stage. */
|
|
3016
|
+
output: unknown;
|
|
3017
|
+
}
|
|
3018
|
+
/**
|
|
3019
|
+
* Assembled timeline (`GET /v1/pipelines/:id/timeline`) — ordered scene
|
|
3020
|
+
* composites + their durations, plus optional music/narration and live
|
|
3021
|
+
* per-shot animate progress. The data the studio turns into a render.
|
|
3022
|
+
*/
|
|
3023
|
+
interface PipelineTimeline {
|
|
3024
|
+
fps: number;
|
|
3025
|
+
width: number;
|
|
3026
|
+
height: number;
|
|
3027
|
+
scenes: Array<{
|
|
3028
|
+
compositeUrl: string;
|
|
3029
|
+
durationSeconds: number;
|
|
3030
|
+
}>;
|
|
3031
|
+
musicUrl?: string;
|
|
3032
|
+
narrationUrl?: string;
|
|
3033
|
+
animateProgress?: {
|
|
3034
|
+
totalShots: number;
|
|
3035
|
+
shotsDone: number;
|
|
3036
|
+
percent: number;
|
|
3037
|
+
};
|
|
3038
|
+
}
|
|
3039
|
+
interface BranchPipelineInput {
|
|
3040
|
+
/** The stage to re-run from. Upstream stages are cloned as approved. */
|
|
3041
|
+
fromStage: PipelineStageName;
|
|
3042
|
+
}
|
|
3043
|
+
interface BranchPipelineResult {
|
|
3044
|
+
/** The id of the newly created pipeline. */
|
|
3045
|
+
pipelineId: string;
|
|
3046
|
+
/** Stage names that were cloned as 'approved' (stages before `fromStage`). */
|
|
3047
|
+
clonedStages: string[];
|
|
3048
|
+
/** Number of entity rows cloned into the new pipeline. */
|
|
3049
|
+
clonedEntities: number;
|
|
3050
|
+
}
|
|
3051
|
+
/**
|
|
3052
|
+
* A single chat turn returned by `getStageChat`. Mirrors the
|
|
3053
|
+
* `pipeline_chat_turns` row shape selected by the GET handler.
|
|
3054
|
+
*
|
|
3055
|
+
* `@nodaro/shared` does not yet export a Zod schema for the full row; it only
|
|
3056
|
+
* exports `ChatTurnResponseSchema` (the LLM response shape) and the
|
|
3057
|
+
* `ProposedChange` discriminated union. Define the wire-format row locally so
|
|
3058
|
+
* callers get end-to-end typing today without re-shaping the backend payload.
|
|
3059
|
+
*/
|
|
3060
|
+
interface ChatTurn {
|
|
3061
|
+
id: string;
|
|
3062
|
+
turn_n: number;
|
|
3063
|
+
role: "user" | "assistant";
|
|
3064
|
+
content: string;
|
|
3065
|
+
proposed_change: ProposedChange | null;
|
|
3066
|
+
llm_call_id: string | null;
|
|
3067
|
+
applied_to_attempt_id: string | null;
|
|
3068
|
+
created_at: string;
|
|
3069
|
+
}
|
|
3070
|
+
/**
|
|
3071
|
+
* Result of `chatStage` — the assistant turn that was just persisted. The
|
|
3072
|
+
* route always echoes the assistant message back so callers can render the
|
|
3073
|
+
* reply without a follow-up GET (SSE is the secondary delivery channel).
|
|
3074
|
+
*/
|
|
3075
|
+
interface ChatStageResult {
|
|
3076
|
+
turnId: string;
|
|
3077
|
+
role: "assistant";
|
|
3078
|
+
content: string;
|
|
3079
|
+
proposed_change: ProposedChange | null;
|
|
3080
|
+
}
|
|
3081
|
+
/**
|
|
3082
|
+
* Result of `applyChatProposal`. Discriminated on `applied`:
|
|
3083
|
+
*
|
|
3084
|
+
* - `applied: true` — `applyStageEdit` validated + persisted a new attempt and
|
|
3085
|
+
* flipped the stage to approved. `newOutput` is the post-patch artifact.
|
|
3086
|
+
* - `applied: false` — recoverable failure (schema_invalid or
|
|
3087
|
+
* reference_integrity_failed); the backend has already inserted a follow-up
|
|
3088
|
+
* assistant turn with a human-readable hint so the user can iterate via
|
|
3089
|
+
* chat. Hard failures throw via the client's error pipeline (409).
|
|
3090
|
+
*/
|
|
3091
|
+
type ApplyChatProposalResult = {
|
|
3092
|
+
applied: true;
|
|
3093
|
+
attemptId: string;
|
|
3094
|
+
newOutput: unknown;
|
|
3095
|
+
} | {
|
|
3096
|
+
applied: false;
|
|
3097
|
+
error: {
|
|
3098
|
+
code: string;
|
|
3099
|
+
detail?: unknown;
|
|
3100
|
+
};
|
|
3101
|
+
};
|
|
3102
|
+
declare class PipelinesResource {
|
|
3103
|
+
private client;
|
|
3104
|
+
constructor(client: NodaroClient);
|
|
3105
|
+
/**
|
|
3106
|
+
* Start a new pipeline (headless film generation) — the programmatic
|
|
3107
|
+
* equivalent of the studio's "Create film". In Auto mode the engine
|
|
3108
|
+
* self-advances to completion; poll {@link get} for status and
|
|
3109
|
+
* {@link getTimeline} for the assembled output. In manual/guided mode, drive
|
|
3110
|
+
* it with {@link pendingApprovals} + {@link approveStage} /
|
|
3111
|
+
* {@link approveSubGate}.
|
|
3112
|
+
*
|
|
3113
|
+
* Requires `pipelines:execute` scope. Returns the new pipeline id.
|
|
3114
|
+
*/
|
|
3115
|
+
create(input: PipelineInput): Promise<{
|
|
3116
|
+
id: string;
|
|
3117
|
+
}>;
|
|
3118
|
+
/**
|
|
3119
|
+
* Fetch current pipeline state: `status`, `current_stage`, credit counters,
|
|
3120
|
+
* `mode`, and `failure_reason` (set when `status='failed'`). Poll this to
|
|
3121
|
+
* track a headless Auto run to completion. Requires `pipelines:read`.
|
|
3122
|
+
*/
|
|
3123
|
+
get(id: string): Promise<PipelineRecord>;
|
|
3124
|
+
/** List the caller's pipelines (most recent first). Requires `pipelines:read`. */
|
|
3125
|
+
list(): Promise<PipelineRecord[]>;
|
|
3126
|
+
/**
|
|
3127
|
+
* Cancel a running pipeline. Unspent reserved credits refund. Idempotent on
|
|
3128
|
+
* an already-terminal pipeline. Requires `pipelines:execute`.
|
|
3129
|
+
*/
|
|
3130
|
+
cancel(id: string): Promise<{
|
|
3131
|
+
ok: true;
|
|
3132
|
+
}>;
|
|
3133
|
+
/**
|
|
3134
|
+
* Stages currently `awaiting_approval`. Empty in a clean Auto run (the engine
|
|
3135
|
+
* self-approves); populated in manual/guided mode at each gate. Requires
|
|
3136
|
+
* `pipelines:read`.
|
|
3137
|
+
*/
|
|
3138
|
+
pendingApprovals(id: string): Promise<PendingApproval[]>;
|
|
3139
|
+
/**
|
|
3140
|
+
* Approve a stage so the engine advances to the next one. An optional `edits`
|
|
3141
|
+
* JSON-Patch is applied to the stage output before approval. Requires
|
|
3142
|
+
* `pipelines:approve`.
|
|
3143
|
+
*/
|
|
3144
|
+
approveStage(id: string, stage: PipelineStageName, edits?: unknown): Promise<{
|
|
3145
|
+
ok: true;
|
|
3146
|
+
}>;
|
|
3147
|
+
/**
|
|
3148
|
+
* Reject a stage with feedback; the engine re-runs it incorporating the note.
|
|
3149
|
+
* Requires `pipelines:approve`.
|
|
3150
|
+
*/
|
|
3151
|
+
rejectStage(id: string, stage: PipelineStageName, feedback: string): Promise<{
|
|
3152
|
+
ok: true;
|
|
3153
|
+
}>;
|
|
3154
|
+
/**
|
|
3155
|
+
* Approve a Stage-7 sub-gate (`dialogue_recheck` / `silent_cut`) so the
|
|
3156
|
+
* orchestrator resumes from the next sub-step. Requires `pipelines:approve`.
|
|
3157
|
+
*/
|
|
3158
|
+
approveSubGate(id: string, gate: SubGateName): Promise<{
|
|
3159
|
+
ok: true;
|
|
3160
|
+
gate: SubGateName;
|
|
3161
|
+
resumed_at: string;
|
|
3162
|
+
}>;
|
|
3163
|
+
/**
|
|
3164
|
+
* Read a single stage's `status`, `output`, and `critic_feedback`. Useful for
|
|
3165
|
+
* inspecting the script/plan before approving. Requires `pipelines:read`.
|
|
3166
|
+
*/
|
|
3167
|
+
getStage(id: string, stage: PipelineStageName): Promise<{
|
|
3168
|
+
status: string;
|
|
3169
|
+
output: unknown;
|
|
3170
|
+
critic_feedback: unknown;
|
|
3171
|
+
}>;
|
|
3172
|
+
/**
|
|
3173
|
+
* Assembled timeline — ordered scene composites + durations + audio URLs +
|
|
3174
|
+
* live animate progress. The output a headless caller renders or hands to a
|
|
3175
|
+
* downstream editor. Requires `pipelines:read`.
|
|
3176
|
+
*/
|
|
3177
|
+
getTimeline(id: string): Promise<PipelineTimeline>;
|
|
3178
|
+
/**
|
|
3179
|
+
* Branch a completed pipeline into a new pipeline that re-runs from the
|
|
3180
|
+
* given stage. The original pipeline's upstream stages and entities are
|
|
3181
|
+
* cloned into the new pipeline; downstream stages are created by the
|
|
3182
|
+
* orchestrator as it advances.
|
|
3183
|
+
*
|
|
3184
|
+
* Requires `pipelines:execute` scope.
|
|
3185
|
+
* The source pipeline must have `status='completed'`.
|
|
3186
|
+
*
|
|
3187
|
+
* @returns 201 with `{ pipelineId, clonedStages, clonedEntities }`.
|
|
3188
|
+
*/
|
|
3189
|
+
branch(id: string, input: BranchPipelineInput): Promise<BranchPipelineResult>;
|
|
3190
|
+
/**
|
|
3191
|
+
* Send a chat message to the Showrunner Refinement Director (Guided Mode).
|
|
3192
|
+
* Persists user + assistant turns; returns the assistant's reply and an
|
|
3193
|
+
* optional `proposed_change` the user can `applyChatProposal` to commit.
|
|
3194
|
+
*
|
|
3195
|
+
* Requires `pipelines:approve` scope. The pipeline must have
|
|
3196
|
+
* `mode='guided'` and the stage must be `awaiting_approval`.
|
|
3197
|
+
*
|
|
3198
|
+
* Only the Script stage ships a wired specialist in Phase 1D.2b — the other
|
|
3199
|
+
* chat-enabled stages (`shot_list`, `post_merge`) return 501 until 1D.2d.
|
|
3200
|
+
*/
|
|
3201
|
+
chatStage(pipelineId: string, stage: ChatEnabledStage, message: string): Promise<ChatStageResult>;
|
|
3202
|
+
/**
|
|
3203
|
+
* Accept a proposed change from a prior assistant turn. Routes through
|
|
3204
|
+
* `applyStageEdit` (validates JSON Patch + per-stage schema +
|
|
3205
|
+
* reference-integrity, inserts a new pipeline_stage_attempts row, CAS-flips
|
|
3206
|
+
* the stage to approved, emits `chat:proposal_applied` SSE).
|
|
3207
|
+
*
|
|
3208
|
+
* Requires `pipelines:approve` scope.
|
|
3209
|
+
*
|
|
3210
|
+
* Returns `{ applied: true, attemptId, newOutput }` on success, or
|
|
3211
|
+
* `{ applied: false, error }` on recoverable failures (the backend already
|
|
3212
|
+
* inserted a follow-up assistant turn with a hint). Hard failures
|
|
3213
|
+
* (`patch_invalid`, `stage_not_awaiting`) throw via the standard error
|
|
3214
|
+
* pipeline (HTTP 409).
|
|
3215
|
+
*/
|
|
3216
|
+
applyChatProposal(pipelineId: string, stage: ChatEnabledStage, turnId: string): Promise<ApplyChatProposalResult>;
|
|
3217
|
+
/**
|
|
3218
|
+
* Fetch the chat history for a stage. Returns an empty array when no turns
|
|
3219
|
+
* exist yet (e.g., stage has not been started or the user hasn't sent any
|
|
3220
|
+
* messages). Used by the frontend chat panel on initial mount; subsequent
|
|
3221
|
+
* updates arrive via SSE (`chat:turn` events).
|
|
3222
|
+
*
|
|
3223
|
+
* Requires `pipelines:read` scope.
|
|
3224
|
+
*/
|
|
3225
|
+
getStageChat(pipelineId: string, stage: ChatEnabledStage): Promise<{
|
|
3226
|
+
turns: ChatTurn[];
|
|
3227
|
+
}>;
|
|
3228
|
+
}
|
|
3229
|
+
|
|
3230
|
+
interface ReduceInput {
|
|
3231
|
+
/** Which fan-in strategy to run. */
|
|
3232
|
+
strategyId: ReduceStrategyId;
|
|
3233
|
+
/**
|
|
3234
|
+
* Strategy-specific config. Defaults to `{}` server-side, which uses every
|
|
3235
|
+
* strategy's `defaultConfig`. Schemas (from `@nodaro/shared`):
|
|
3236
|
+
* - `pick-best-llm`: `{ criteria: string, inputKind?: "text" | "image-url" }`
|
|
3237
|
+
* - `concat`: `{ separator?: string }`
|
|
3238
|
+
* - `vote`: `{ caseSensitive?: boolean }`
|
|
3239
|
+
* - `merge-json`: `{ strategy?: "deep" | "shallow" }`
|
|
3240
|
+
* - `first-non-empty`, `count`: `{}`
|
|
3241
|
+
*/
|
|
3242
|
+
strategyConfig?: Record<string, unknown>;
|
|
3243
|
+
/** Up to 1000 input strings (URLs, text fragments, etc.). */
|
|
3244
|
+
inputs: string[];
|
|
3245
|
+
/**
|
|
3246
|
+
* Optional — associates this reduce run with a workflow execution. The
|
|
3247
|
+
* server reads this from the body before Zod strips it (same path as
|
|
3248
|
+
* other job-creating routes).
|
|
3249
|
+
*/
|
|
3250
|
+
workflowId?: string;
|
|
3251
|
+
}
|
|
3252
|
+
interface ReduceResult {
|
|
3253
|
+
jobId: string;
|
|
3254
|
+
/**
|
|
3255
|
+
* Stringified result — for `count` this is a numeric string, for
|
|
3256
|
+
* `merge-json` this is the JSON-encoded merged object, otherwise the
|
|
3257
|
+
* chosen / joined text.
|
|
3258
|
+
*/
|
|
3259
|
+
output: string;
|
|
3260
|
+
meta: ReduceMeta;
|
|
3261
|
+
}
|
|
3262
|
+
declare class ReduceResource {
|
|
3263
|
+
private client;
|
|
3264
|
+
constructor(client: NodaroClient);
|
|
3265
|
+
/**
|
|
3266
|
+
* Run the Reduce (fan-in) node directly — useful for scripted batch
|
|
3267
|
+
* scoring, picking the best of N generations outside a workflow, or
|
|
3268
|
+
* one-shot programmatic merges.
|
|
3269
|
+
*
|
|
3270
|
+
* Throws `NodaroError` on 4xx/5xx responses (e.g. `code: "no_valid_inputs"`
|
|
3271
|
+
* with status 400 when every input is empty / whitespace; the underlying
|
|
3272
|
+
* `EmptyInputError` is mapped to a 400 server-side).
|
|
3273
|
+
*/
|
|
3274
|
+
run(input: ReduceInput): Promise<ReduceResult>;
|
|
3275
|
+
}
|
|
3276
|
+
|
|
3277
|
+
interface CommonInput {
|
|
3278
|
+
nodeType: string;
|
|
3279
|
+
provider?: string;
|
|
3280
|
+
style?: string;
|
|
3281
|
+
aspectRatio?: string;
|
|
3282
|
+
duration?: number;
|
|
3283
|
+
llmModel?: string;
|
|
3284
|
+
nodeContext?: WizardNodeContext;
|
|
3285
|
+
userPreference?: string;
|
|
3286
|
+
/** Associates this call with a workflow execution. Read server-side before Zod. */
|
|
3287
|
+
workflowId?: string;
|
|
3288
|
+
}
|
|
3289
|
+
interface AnalyzeInput extends CommonInput {
|
|
3290
|
+
/** The user's rough idea. Omit to build questions from scratch. */
|
|
3291
|
+
prompt?: string;
|
|
3292
|
+
}
|
|
3293
|
+
interface AnalyzeResult {
|
|
3294
|
+
jobId: string;
|
|
3295
|
+
questions: WizardQuestion[];
|
|
3296
|
+
}
|
|
3297
|
+
interface GenerateInput extends CommonInput {
|
|
3298
|
+
/** The chosen answers from analyze. */
|
|
3299
|
+
selections: WizardSelection[];
|
|
3300
|
+
/** The user's original rough idea, woven into the generated prompt. */
|
|
3301
|
+
originalPrompt?: string;
|
|
3302
|
+
}
|
|
3303
|
+
interface EnhanceInput extends CommonInput {
|
|
3304
|
+
/** The rough idea to improve one-shot. Omit to build from scratch. */
|
|
3305
|
+
prompt?: string;
|
|
3306
|
+
}
|
|
3307
|
+
interface PromptResult {
|
|
3308
|
+
jobId: string;
|
|
3309
|
+
prompt: string;
|
|
3310
|
+
recommendedModel?: RecommendedModel;
|
|
3311
|
+
}
|
|
3312
|
+
/**
|
|
3313
|
+
* AI Prompt Wizard — help write/improve prompts for generation nodes.
|
|
3314
|
+
*
|
|
3315
|
+
* - `analyze` -> guided questions, `generate` -> prompt from selections (the
|
|
3316
|
+
* 2-step human flow), or `enhance` -> one-shot "improve this prompt".
|
|
3317
|
+
*
|
|
3318
|
+
* All three delegate to `POST /v1/prompt-helper/wizard`. Throws `NodaroError`
|
|
3319
|
+
* on 4xx/5xx (e.g. `validation_error`, `malformed_response`).
|
|
3320
|
+
*/
|
|
3321
|
+
declare class PromptHelperResource {
|
|
3322
|
+
private client;
|
|
3323
|
+
constructor(client: NodaroClient);
|
|
3324
|
+
analyze(input: AnalyzeInput): Promise<AnalyzeResult>;
|
|
3325
|
+
generate(input: GenerateInput): Promise<PromptResult>;
|
|
3326
|
+
enhance(input: EnhanceInput): Promise<PromptResult>;
|
|
3327
|
+
}
|
|
3328
|
+
|
|
3329
|
+
/**
|
|
3330
|
+
* Re-export the shared voice types so SDK consumers don't have to add
|
|
3331
|
+
* `@nodaro/shared` as a second dependency just to type a `Voice` row, a
|
|
3332
|
+
* `VoiceClone`, or a `searchLibrary` call. Single source of truth lives in
|
|
3333
|
+
* `@nodaro/shared`.
|
|
3334
|
+
*/
|
|
3335
|
+
/** Audio-FX preset union (reverb spaces / telephone / megaphone / echo / custom) — used by {@link VoiceChangerProInput.voiceFx}. */
|
|
3336
|
+
/**
|
|
3337
|
+
* Read access to ElevenLabs voices: the premade catalog plus the shared
|
|
3338
|
+
* community Voice Library (both public GETs, no body), and the signed-in
|
|
3339
|
+
* user's own voice clones (list / create-from-url / delete).
|
|
3340
|
+
*/
|
|
3341
|
+
declare class VoicesResource {
|
|
3342
|
+
private client;
|
|
3343
|
+
constructor(client: NodaroClient);
|
|
3344
|
+
/**
|
|
3345
|
+
* List the premade ElevenLabs voices (`GET /v1/voices`). Falls back to a
|
|
3346
|
+
* curated set server-side when no ElevenLabs API key is configured.
|
|
3347
|
+
*/
|
|
3348
|
+
list(): Promise<Voice[]>;
|
|
3349
|
+
/**
|
|
3350
|
+
* Search the shared/community Voice Library (`GET /v1/voices/library`). All
|
|
3351
|
+
* params are optional and forwarded as a querystring; `undefined` / `null` /
|
|
3352
|
+
* empty-string values are omitted so the server defaults apply. `hasMore`
|
|
3353
|
+
* drives "load more" pagination.
|
|
3354
|
+
*/
|
|
3355
|
+
searchLibrary(params?: VoiceLibraryParams): Promise<VoiceLibraryResponse>;
|
|
3356
|
+
/**
|
|
3357
|
+
* List the signed-in user's voice clones (`GET /v1/voice-clones`). The route
|
|
3358
|
+
* wraps the rows in `{ voiceClones }`; we unwrap to the bare array.
|
|
3359
|
+
*/
|
|
3360
|
+
listClones(): Promise<VoiceClone[]>;
|
|
3361
|
+
/**
|
|
3362
|
+
* Clone a voice from an already-uploaded audio URL
|
|
3363
|
+
* (`POST /v1/voice-clones/from-url`). Costs credits. Returns the create
|
|
3364
|
+
* subset of `VoiceClone` (`elevenlabsVoiceId` is the id to use at
|
|
3365
|
+
* text-to-speech time).
|
|
3366
|
+
*/
|
|
3367
|
+
createClone(input: {
|
|
3368
|
+
name: string;
|
|
3369
|
+
audioUrl: string;
|
|
3370
|
+
}): Promise<VoiceClone>;
|
|
3371
|
+
/** Delete one of the user's voice clones (`DELETE /v1/voice-clones/:id`). */
|
|
3372
|
+
deleteClone(id: string): Promise<void>;
|
|
3373
|
+
/**
|
|
3374
|
+
* Replace the voice in a recording — or in a whole talking video — with a
|
|
3375
|
+
* different voice (`POST /v1/voice-changer`). Pass `audioUrl` to revoice
|
|
3376
|
+
* audio→audio, or `videoUrl` to revoice an entire clip (the server demuxes
|
|
3377
|
+
* the audio, runs speech-to-speech, and remuxes onto the original video,
|
|
3378
|
+
* returning the video plus the new audio track). Exactly one of `audioUrl` /
|
|
3379
|
+
* `videoUrl` is required; when both are sent, video wins. `removeBackgroundNoise`
|
|
3380
|
+
* off keeps the music/SFX bed under the new voice; on yields a clean voice-only
|
|
3381
|
+
* result. Costs credits and runs async — poll `jobs.get(jobId)` for the result
|
|
3382
|
+
* (`output_data.videoUrl` + `output_data.audioUrl` in video mode).
|
|
3383
|
+
*/
|
|
3384
|
+
change(input: {
|
|
3385
|
+
voiceId: string;
|
|
3386
|
+
audioUrl?: string;
|
|
3387
|
+
videoUrl?: string;
|
|
3388
|
+
stability?: number;
|
|
3389
|
+
similarityBoost?: number;
|
|
3390
|
+
/** Style exaggeration (0–1). Default 0; >0 amplifies delivery at the cost of latency/stability. */
|
|
3391
|
+
style?: number;
|
|
3392
|
+
removeBackgroundNoise?: boolean;
|
|
3393
|
+
}): Promise<{
|
|
3394
|
+
jobId: string;
|
|
3395
|
+
}>;
|
|
3396
|
+
/**
|
|
3397
|
+
* Recast each detected speaker in a multi-speaker recording to a different
|
|
3398
|
+
* voice (`POST /v1/voice-changer-pro`). `orderedVoices` maps speaker positions to
|
|
3399
|
+
* voices in detection order — speaker 0 → `orderedVoices[0]`, speaker 1 →
|
|
3400
|
+
* `orderedVoices[1]`, etc. Speakers beyond the end of `orderedVoices` keep
|
|
3401
|
+
* their original voice. Each entry is EITHER a bare voice id (premade name or
|
|
3402
|
+
* ElevenLabs UUID) OR a {@link VoiceChangerProVoice} object carrying per-voice
|
|
3403
|
+
* ElevenLabs speech-to-speech settings (stability / similarityBoost / style /
|
|
3404
|
+
* useSpeakerBoost / `seed`) plus a loudness `volumeMode` (and a manual
|
|
3405
|
+
* `volume`). A per-voice `seed` makes that speaker's recast reproducible.
|
|
3406
|
+
*
|
|
3407
|
+
* Pass `audioUrl` for audio-only recast or `videoUrl` to recast the audio
|
|
3408
|
+
* track of a video clip (the server demuxes, recasts, and remuxes).
|
|
3409
|
+
*
|
|
3410
|
+
* Voice and music are ALWAYS separated first — ElevenLabs only ever sees the
|
|
3411
|
+
* isolated vocal stem, never the music bed. `preserveBackground` (default
|
|
3412
|
+
* `true`) only controls whether that music/instrumental stem is mixed back
|
|
3413
|
+
* under the new voices; set it `false` for a clean voice-only result.
|
|
3414
|
+
* `separationQuality` selects the demucs model used for that split: `"fast"`
|
|
3415
|
+
* (default, htdemucs — preserves more of the voice) or `"best"` (htdemucs_ft —
|
|
3416
|
+
* finer separation). `removeBackgroundNoise` additionally denoises the result.
|
|
3417
|
+
* `musicVolumeMode` sets the level of that preserved background (only relevant
|
|
3418
|
+
* when `preserveBackground` is on): `"match"` (default) keeps the original
|
|
3419
|
+
* level, `"normalize"` loudnorms it, `"manual"` uses `musicVolume`%.
|
|
3420
|
+
* `voiceFx` applies a reverb/echo to the COMBINED recast voices BEFORE the
|
|
3421
|
+
* background is mixed back in (effect sits on the voices, not the music bed).
|
|
3422
|
+
*
|
|
3423
|
+
* Cloud-only — costs credits and runs async; poll `jobs.get(jobId)` for the
|
|
3424
|
+
* result (`output_data.videoUrl` + `output_data.audioUrl` in video mode).
|
|
3425
|
+
*/
|
|
3426
|
+
recast(input: VoiceChangerProInput): Promise<{
|
|
3427
|
+
jobId: string;
|
|
3428
|
+
}>;
|
|
3429
|
+
}
|
|
3430
|
+
/**
|
|
3431
|
+
* One entry in {@link VoiceChangerProInput.orderedVoices}. Either a bare voice id
|
|
3432
|
+
* (premade name like `"Rachel"` or an ElevenLabs UUID for a custom clone), or
|
|
3433
|
+
* an object pinning per-voice speech-to-speech settings and the recast's
|
|
3434
|
+
* loudness behaviour for that speaker.
|
|
3435
|
+
*/
|
|
3436
|
+
type VoiceChangerProVoice = string | {
|
|
3437
|
+
/** Target voice — premade name (`"Rachel"`, `"Aria"`, …) or an ElevenLabs UUID for a custom clone. */
|
|
3438
|
+
voiceId: string;
|
|
3439
|
+
/** ElevenLabs stability (0–1). Higher = steadier, lower = more expressive. */
|
|
3440
|
+
stability?: number;
|
|
3441
|
+
/** ElevenLabs similarity boost (0–1) — how closely the output hugs the target voice's timbre. */
|
|
3442
|
+
similarityBoost?: number;
|
|
3443
|
+
/** Style exaggeration (0–1). Default 0; >0 amplifies delivery at the cost of latency / stability. */
|
|
3444
|
+
style?: number;
|
|
3445
|
+
/** ElevenLabs speaker boost — sharpens fidelity to the target speaker. */
|
|
3446
|
+
useSpeakerBoost?: boolean;
|
|
3447
|
+
/**
|
|
3448
|
+
* Deterministic speech-to-speech seed (integer 0–4294967295) for
|
|
3449
|
+
* reproducible per-voice output — the same source + settings + seed
|
|
3450
|
+
* recast this speaker identically across runs. Omit for a random seed.
|
|
3451
|
+
*/
|
|
3452
|
+
seed?: number;
|
|
3453
|
+
/**
|
|
3454
|
+
* Loudness handling for this recast voice. `"match"` (default) matches the
|
|
3455
|
+
* original speaker's loudness; `"normalize"` applies EBU R128 loudnorm;
|
|
3456
|
+
* `"manual"` uses `volume` as a percentage.
|
|
3457
|
+
*/
|
|
3458
|
+
volumeMode?: "match" | "normalize" | "manual";
|
|
3459
|
+
/** Manual output volume as a percentage (0–200). Consulted only when `volumeMode === "manual"`. */
|
|
3460
|
+
volume?: number;
|
|
3461
|
+
};
|
|
3462
|
+
/** Input for {@link VoicesResource.recast}. */
|
|
3463
|
+
interface VoiceChangerProInput {
|
|
3464
|
+
/** URL of an audio file to recast (audio → audio). Exactly one of `audioUrl` / `videoUrl` is required. */
|
|
3465
|
+
audioUrl?: string;
|
|
3466
|
+
/** URL of a video file to recast (the audio track is recast and remuxed). Exactly one of `audioUrl` / `videoUrl` is required. */
|
|
3467
|
+
videoUrl?: string;
|
|
3468
|
+
/**
|
|
3469
|
+
* Voices in speaker-detection order. Speaker N is mapped to `orderedVoices[N]`;
|
|
3470
|
+
* speakers beyond the array keep their original voice. Each entry is a bare
|
|
3471
|
+
* voice id OR a {@link VoiceChangerProVoice} object with per-voice settings.
|
|
3472
|
+
*/
|
|
3473
|
+
orderedVoices: Array<VoiceChangerProVoice>;
|
|
3474
|
+
/** Model to use for speech-to-speech. Defaults to the server-configured default when omitted. */
|
|
3475
|
+
model?: string;
|
|
3476
|
+
/**
|
|
3477
|
+
* Mix the separated music / SFX stem back under the recast voices. Default
|
|
3478
|
+
* `true`. The voice is ALWAYS split out before recasting regardless of this
|
|
3479
|
+
* flag — `false` simply drops the music for a clean voice-only result.
|
|
3480
|
+
*/
|
|
3481
|
+
preserveBackground?: boolean;
|
|
3482
|
+
/**
|
|
3483
|
+
* Demucs model used to split voice from music. `"fast"` (default, htdemucs —
|
|
3484
|
+
* preserves more of the voice) or `"best"` (htdemucs_ft — finer separation).
|
|
3485
|
+
*/
|
|
3486
|
+
separationQuality?: "fast" | "best";
|
|
3487
|
+
/** Strip background noise for a clean voice-only result. */
|
|
3488
|
+
removeBackgroundNoise?: boolean;
|
|
3489
|
+
/**
|
|
3490
|
+
* Level of the preserved background music / SFX stem in the final mix. Only
|
|
3491
|
+
* relevant when `preserveBackground` is on (otherwise there is no background to
|
|
3492
|
+
* level). `"match"` (default) leaves the separated instrumental at its original
|
|
3493
|
+
* level; `"normalize"` applies EBU R128 loudnorm; `"manual"` sets its level to
|
|
3494
|
+
* `musicVolume`%.
|
|
3495
|
+
*/
|
|
3496
|
+
musicVolumeMode?: "match" | "normalize" | "manual";
|
|
3497
|
+
/** Background music level as a percentage (0–200). Consulted only when `musicVolumeMode === "manual"`. */
|
|
3498
|
+
musicVolume?: number;
|
|
3499
|
+
/**
|
|
3500
|
+
* Node-level reverb/echo applied to the COMBINED recast voices **before** the
|
|
3501
|
+
* background is mixed back in (so the effect sits on the voices only, not the
|
|
3502
|
+
* music/SFX bed). Reverb presets (`room`, `hall`, `church`, …) use
|
|
3503
|
+
* `wetDryMix`; the `echo` / `custom` presets use `delayMs` + `decay`. Omit for
|
|
3504
|
+
* no effect.
|
|
3505
|
+
*/
|
|
3506
|
+
voiceFx?: {
|
|
3507
|
+
/** Effect preset — reverb space, `telephone`, `megaphone`, `echo`, or `custom`. */
|
|
3508
|
+
preset: AudioFxPreset;
|
|
3509
|
+
/** Reverb wet/dry mix as a percentage (0–100). Higher = wetter (more reverb). */
|
|
3510
|
+
wetDryMix?: number;
|
|
3511
|
+
/** Echo delay in milliseconds (20–2000). Used by the `echo` / `custom` presets. */
|
|
3512
|
+
delayMs?: number;
|
|
3513
|
+
/** Echo decay / feedback (0–1). Higher = more repeats. Used by the `echo` / `custom` presets. */
|
|
3514
|
+
decay?: number;
|
|
3515
|
+
};
|
|
3516
|
+
}
|
|
3517
|
+
|
|
3518
|
+
/**
|
|
3519
|
+
* Authenticated user's credit balance — the shape of `GET /v1/user/credits`'s
|
|
3520
|
+
* `data` field. Mirrors the canonical `UserBalance` from the backend billing
|
|
3521
|
+
* module (`backend/src/ee/billing/credits.ts`), the source of truth: keep this
|
|
3522
|
+
* in sync if that interface changes.
|
|
3523
|
+
*/
|
|
3524
|
+
interface UserBalance {
|
|
3525
|
+
total: number;
|
|
3526
|
+
subscription: number;
|
|
3527
|
+
topup: number;
|
|
3528
|
+
dailySpent: number;
|
|
3529
|
+
dailyLimit: number | null;
|
|
3530
|
+
monthlyAllocation: number;
|
|
3531
|
+
tier: string;
|
|
3532
|
+
features: Record<string, unknown>;
|
|
3533
|
+
periodEnd: string | null;
|
|
3534
|
+
/** Credits earned for app usage (free tier only — earned by running flows). */
|
|
3535
|
+
appCreditsAllowance: number;
|
|
3536
|
+
}
|
|
3537
|
+
/**
|
|
3538
|
+
* Result of `POST /v1/credits/model-costs` — a batch cost lookup for editor
|
|
3539
|
+
* cost previews. `data` maps each priced identifier → its credit cost.
|
|
3540
|
+
*
|
|
3541
|
+
* Per-model fault isolation (the route runs the lookups under
|
|
3542
|
+
* `Promise.allSettled`): identifiers with no pricing row are reported in
|
|
3543
|
+
* `missing` (undisplayable until an operator seeds a price) and lookup
|
|
3544
|
+
* failures in `errors`, instead of failing the whole batch. Callers typically
|
|
3545
|
+
* render `'—'` for any identifier that lands in `missing`. The hard-fail
|
|
3546
|
+
* policy still triggers at reservation time when the user actually runs the
|
|
3547
|
+
* node — this preview lookup is intentionally lenient.
|
|
3548
|
+
*/
|
|
3549
|
+
interface ModelCostsResult {
|
|
3550
|
+
data: Record<string, number>;
|
|
3551
|
+
missing: string[];
|
|
3552
|
+
errors: string[];
|
|
3553
|
+
}
|
|
3554
|
+
declare class CreditsResource {
|
|
3555
|
+
private client;
|
|
3556
|
+
constructor(client: NodaroClient);
|
|
3557
|
+
/**
|
|
3558
|
+
* `GET /v1/user/credits` → the authenticated user's credit balance and tier
|
|
3559
|
+
* info. Throws `UnauthorizedError` (401) when signed out, and the SDK's
|
|
3560
|
+
* other typed errors on the usual statuses.
|
|
3561
|
+
*/
|
|
3562
|
+
balance(): Promise<UserBalance>;
|
|
3563
|
+
/**
|
|
3564
|
+
* `POST /v1/credits/model-costs` → per-identifier credit cost, for editor
|
|
3565
|
+
* cost previews. Capped at the first {@link MODEL_COSTS_LIMIT} identifiers
|
|
3566
|
+
* (the route's request limit). Preserves the `{ data, missing, errors }`
|
|
3567
|
+
* fault-isolation shape verbatim (see {@link ModelCostsResult}).
|
|
3568
|
+
*/
|
|
3569
|
+
modelCosts(ids: string[]): Promise<ModelCostsResult>;
|
|
3570
|
+
}
|
|
3571
|
+
|
|
3572
|
+
/**
|
|
3573
|
+
* Result of a successful `POST /v1/upload`. Mirrors the route's `data` envelope
|
|
3574
|
+
* (`backend/src/routes/upload.ts`), the source of truth — keep in sync if that
|
|
3575
|
+
* response changes. The route also returns loosely-typed extracted `metadata`;
|
|
3576
|
+
* it is omitted here until a consumer needs a typed shape (add it shaped to
|
|
3577
|
+
* that need rather than inventing `Record<string, unknown>` surface now).
|
|
3578
|
+
*/
|
|
3579
|
+
interface UploadResult {
|
|
3580
|
+
/** Public R2 URL of the stored asset (always present on success). */
|
|
3581
|
+
readonly url: string;
|
|
3582
|
+
/** Storage row id; `null` when no asset row was written (e.g. unauthenticated). */
|
|
3583
|
+
readonly assetId: string | null;
|
|
3584
|
+
/** Generated thumbnail URL (images/video); `null` for audio or on failure. */
|
|
3585
|
+
readonly thumbnailUrl: string | null;
|
|
3586
|
+
/** Server-classified asset category (e.g. "image" | "video" | "audio"). */
|
|
3587
|
+
readonly category: string;
|
|
3588
|
+
/** Display filename (server override or the original). */
|
|
3589
|
+
readonly filename: string;
|
|
3590
|
+
/** Final MIME type after server normalization. */
|
|
3591
|
+
readonly mimeType: string;
|
|
3592
|
+
/** Stored byte size. */
|
|
3593
|
+
readonly sizeBytes: number;
|
|
3594
|
+
/** R2 object key. */
|
|
3595
|
+
readonly r2Key: string;
|
|
3596
|
+
}
|
|
3597
|
+
declare class UploadsResource {
|
|
3598
|
+
private client;
|
|
3599
|
+
constructor(client: NodaroClient);
|
|
3600
|
+
/**
|
|
3601
|
+
* Upload one file (`POST /v1/upload`, multipart — the file rides the `file`
|
|
3602
|
+
* field). The SDK's `request` detects the `FormData` body and lets the
|
|
3603
|
+
* runtime set the multipart boundary. Returns the persisted asset's public
|
|
3604
|
+
* URL + storage metadata (unwraps the `{ data }` envelope). Throws
|
|
3605
|
+
* `StorageExceededError` (413) over the storage cap and the SDK's other typed
|
|
3606
|
+
* errors on the usual statuses.
|
|
3607
|
+
*/
|
|
3608
|
+
upload(file: File): Promise<UploadResult>;
|
|
3609
|
+
}
|
|
3610
|
+
|
|
3611
|
+
/**
|
|
3612
|
+
* A media asset in the caller's library (`GET /v1/library`). Mirrors the route's
|
|
3613
|
+
* camelCase row shape (`backend/src/routes/library.ts`), the source of truth —
|
|
3614
|
+
* keep in sync if that response changes. Covers BOTH uploaded files and saved
|
|
3615
|
+
* generations; `type` discriminates the media kind.
|
|
3616
|
+
*/
|
|
3617
|
+
interface LibraryAsset {
|
|
3618
|
+
readonly id: string;
|
|
3619
|
+
/** Media kind — filter the list with the same values via `type`. */
|
|
3620
|
+
readonly type: "image" | "video" | "audio";
|
|
3621
|
+
/** Display filename (server override or the original); `null` if unknown. */
|
|
3622
|
+
readonly filename: string | null;
|
|
3623
|
+
readonly mimeType: string | null;
|
|
3624
|
+
readonly sizeBytes: number | null;
|
|
3625
|
+
/** Public R2 URL of the asset. */
|
|
3626
|
+
readonly url: string;
|
|
3627
|
+
/** Generated thumbnail URL (images/video); `null` for audio or when absent. */
|
|
3628
|
+
readonly thumbnailUrl: string | null;
|
|
3629
|
+
readonly metadata: Record<string, unknown>;
|
|
3630
|
+
/** True when promoted to the shared (admin) library. */
|
|
3631
|
+
readonly isLibraryItem: boolean;
|
|
3632
|
+
/** How the asset entered storage (e.g. "manual_upload" | "generated"). */
|
|
3633
|
+
readonly uploadSource: string;
|
|
3634
|
+
readonly createdAt: string;
|
|
3635
|
+
}
|
|
3636
|
+
interface ListLibraryParams {
|
|
3637
|
+
/** Filter by media kind; `"all"` (default) returns every kind. */
|
|
3638
|
+
readonly type?: "all" | "image" | "video" | "audio";
|
|
3639
|
+
/** Case-insensitive filename substring filter. */
|
|
3640
|
+
readonly search?: string;
|
|
3641
|
+
/** Page size, 1–100 (default 40). */
|
|
3642
|
+
readonly limit?: number;
|
|
3643
|
+
/** Opaque cursor from a prior page's `nextCursor` (fetches the next page). */
|
|
3644
|
+
readonly cursor?: string;
|
|
3645
|
+
/**
|
|
3646
|
+
* `true` → EVERY asset the caller owns (the "Storage" view: uploads +
|
|
3647
|
+
* generations, regardless of the in-library flag). `false` (default) → only
|
|
3648
|
+
* assets explicitly saved to the library plus shared items (the in-editor
|
|
3649
|
+
* Media Library picker).
|
|
3650
|
+
*/
|
|
3651
|
+
readonly owned?: boolean;
|
|
3652
|
+
}
|
|
3653
|
+
interface ListLibraryResult {
|
|
3654
|
+
readonly data: LibraryAsset[];
|
|
3655
|
+
/** Pass back as `cursor` for the next page; `null` when there are no more. */
|
|
3656
|
+
readonly nextCursor: string | null;
|
|
3657
|
+
/** Exact total (first page only — omitted on cursor-paged requests). */
|
|
3658
|
+
readonly totalCount?: number;
|
|
3659
|
+
}
|
|
3660
|
+
/**
|
|
3661
|
+
* Library — the caller's media assets (uploaded files + saved generations).
|
|
3662
|
+
*
|
|
3663
|
+
* Read surface over `GET /v1/library`: a cursor-paginated, type-filterable,
|
|
3664
|
+
* filename-searchable list. This is the "bring from your media" source for
|
|
3665
|
+
* pickers that also offer upload + in-production stills. Writes (saving a
|
|
3666
|
+
* generation, promoting/removing) stay on their dedicated routes — not exposed
|
|
3667
|
+
* here until a consumer needs them.
|
|
3668
|
+
*/
|
|
3669
|
+
declare class LibraryResource {
|
|
3670
|
+
private client;
|
|
3671
|
+
constructor(client: NodaroClient);
|
|
3672
|
+
/**
|
|
3673
|
+
* `GET /v1/library` → a page of the caller's media assets (newest first) plus
|
|
3674
|
+
* a `nextCursor`. Pass the returned `nextCursor` back as `cursor` for the next
|
|
3675
|
+
* page. Filter by `type` and a filename `search`; `owned: true` returns the
|
|
3676
|
+
* full Storage set (uploads + generations), the default only library-saved +
|
|
3677
|
+
* shared items.
|
|
3678
|
+
*/
|
|
3679
|
+
list(params?: ListLibraryParams): Promise<ListLibraryResult>;
|
|
3680
|
+
}
|
|
3681
|
+
|
|
3682
|
+
/**
|
|
3683
|
+
* A user's saved custom preset (`GET /v1/node-presets`). Mirrors the backend's
|
|
3684
|
+
* camelCase row shape. `data` is captured node config — merge it into a node's
|
|
3685
|
+
* data when building/running a workflow to "apply" the preset.
|
|
3686
|
+
*/
|
|
3687
|
+
interface NodePreset {
|
|
3688
|
+
id: string;
|
|
3689
|
+
nodeType: string;
|
|
3690
|
+
name: string;
|
|
3691
|
+
description?: string;
|
|
3692
|
+
data: Record<string, unknown>;
|
|
3693
|
+
groupId?: string;
|
|
3694
|
+
tags: string[];
|
|
3695
|
+
sortOrder: number;
|
|
3696
|
+
createdAt: string;
|
|
3697
|
+
updatedAt: string;
|
|
3698
|
+
}
|
|
3699
|
+
/** A user's preset folder/section (`GET /v1/node-preset-groups`). */
|
|
3700
|
+
interface NodePresetGroup {
|
|
3701
|
+
id: string;
|
|
3702
|
+
nodeType: string;
|
|
3703
|
+
name: string;
|
|
3704
|
+
kind: "folder" | "section";
|
|
3705
|
+
sortOrder: number;
|
|
3706
|
+
createdAt: string;
|
|
3707
|
+
updatedAt: string;
|
|
3708
|
+
}
|
|
3709
|
+
/**
|
|
3710
|
+
* Result of `GET /v1/node-presets/factory` — the built-in (factory) catalog for
|
|
3711
|
+
* a node type.
|
|
3712
|
+
*/
|
|
3713
|
+
interface FactoryPresetsResult {
|
|
3714
|
+
data: FactoryPreset[];
|
|
3715
|
+
}
|
|
3716
|
+
/**
|
|
3717
|
+
* Node presets — reusable, named node configurations.
|
|
3718
|
+
*
|
|
3719
|
+
* Read-only over the API today: list your own custom presets and their folders,
|
|
3720
|
+
* and list the built-in factory catalog. To *use* a preset, take its `data` and
|
|
3721
|
+
* merge it into a node's config when you create/update a workflow. (Creating and
|
|
3722
|
+
* editing presets remains in the editor for now.)
|
|
3723
|
+
*/
|
|
3724
|
+
declare class PresetsResource {
|
|
3725
|
+
private client;
|
|
3726
|
+
constructor(client: NodaroClient);
|
|
3727
|
+
/**
|
|
3728
|
+
* `GET /v1/node-presets` → your custom presets, newest first. Pass `nodeType`
|
|
3729
|
+
* (e.g. `"generate-image"`) to filter to one node type.
|
|
3730
|
+
*/
|
|
3731
|
+
list(nodeType?: string): Promise<NodePreset[]>;
|
|
3732
|
+
/**
|
|
3733
|
+
* `GET /v1/node-preset-groups` → your preset folders/sections, in display
|
|
3734
|
+
* order. Pass `nodeType` to filter to one node type.
|
|
3735
|
+
*/
|
|
3736
|
+
listGroups(nodeType?: string): Promise<NodePresetGroup[]>;
|
|
3737
|
+
/**
|
|
3738
|
+
* `GET /v1/node-presets/factory` → the built-in catalog for `nodeType`. These
|
|
3739
|
+
* ship with the app (no account needed to exist), so they're a good starting
|
|
3740
|
+
* point for "what configs are available".
|
|
3741
|
+
*/
|
|
3742
|
+
listFactory(nodeType: string): Promise<FactoryPresetsResult>;
|
|
3743
|
+
}
|
|
3744
|
+
|
|
3745
|
+
/**
|
|
3746
|
+
* Picker-catalog types. Mirrors `@nodaro/shared`'s `ProjectedPickerCatalog` /
|
|
3747
|
+
* `PickerCatalogSummary` so the SDK stays dependency-free (same convention as
|
|
3748
|
+
* `NodeDescriptor` mirroring node-registry).
|
|
3749
|
+
*/
|
|
3750
|
+
interface PickerOption {
|
|
3751
|
+
id: string;
|
|
3752
|
+
label: string;
|
|
3753
|
+
description?: string;
|
|
3754
|
+
category?: string;
|
|
3755
|
+
/** The prompt fragment this id injects downstream. Present only when detail="full". */
|
|
3756
|
+
promptHint?: string;
|
|
3757
|
+
icon?: string;
|
|
3758
|
+
}
|
|
3759
|
+
interface PickerDimension {
|
|
3760
|
+
field: string;
|
|
3761
|
+
label: string;
|
|
3762
|
+
options: PickerOption[];
|
|
3763
|
+
}
|
|
3764
|
+
interface PickerCatalog {
|
|
3765
|
+
nodeType: string;
|
|
3766
|
+
label: string;
|
|
3767
|
+
catalogId: string;
|
|
3768
|
+
kind: "single" | "multi";
|
|
3769
|
+
/** single only — the node-data field the chosen id writes to. */
|
|
3770
|
+
valueField?: string;
|
|
3771
|
+
defaultValue?: string;
|
|
3772
|
+
categoryOrder?: string[];
|
|
3773
|
+
categoryLabels?: Record<string, string>;
|
|
3774
|
+
/** single-dim catalogs. */
|
|
3775
|
+
options?: PickerOption[];
|
|
3776
|
+
/** multi-dim catalogs. */
|
|
3777
|
+
fields?: string[];
|
|
3778
|
+
dimensions?: PickerDimension[];
|
|
3779
|
+
detail?: "compact" | "full";
|
|
3780
|
+
}
|
|
3781
|
+
interface PickerCatalogSummary {
|
|
3782
|
+
nodeType: string;
|
|
3783
|
+
label: string;
|
|
3784
|
+
catalogId: string;
|
|
3785
|
+
kind: "single" | "multi";
|
|
3786
|
+
valueField?: string;
|
|
3787
|
+
fields?: string[];
|
|
3788
|
+
optionCount: number;
|
|
3789
|
+
}
|
|
3790
|
+
interface GetPickerCatalogOptions {
|
|
3791
|
+
/** "compact" (default) = id, label, category, icon; "full" additionally includes description + promptHint. */
|
|
3792
|
+
detail?: "compact" | "full";
|
|
3793
|
+
/** single-dim: filter to one category. */
|
|
3794
|
+
category?: string;
|
|
3795
|
+
/** multi-dim: only this dimension field. */
|
|
3796
|
+
field?: string;
|
|
3797
|
+
}
|
|
3798
|
+
declare class PickerCatalogsResource {
|
|
3799
|
+
private client;
|
|
3800
|
+
constructor(client: NodaroClient);
|
|
3801
|
+
/** List every parameter-picker node type + its option count. Cached publicly 5 min. */
|
|
3802
|
+
list(): Promise<{
|
|
3803
|
+
data: PickerCatalogSummary[];
|
|
3804
|
+
}>;
|
|
3805
|
+
/** Get one picker's catalog of valid values. */
|
|
3806
|
+
get(nodeType: string, opts?: GetPickerCatalogOptions): Promise<{
|
|
3807
|
+
data: PickerCatalog;
|
|
3808
|
+
}>;
|
|
3809
|
+
}
|
|
3810
|
+
|
|
3811
|
+
/**
|
|
3812
|
+
* The community-listing types are the single source of truth in
|
|
3813
|
+
* `@nodaro/shared` (re-used by the backend, frontend, and CLI). Re-export them
|
|
3814
|
+
* here so SDK consumers don't have to add `@nodaro/shared` as a second
|
|
3815
|
+
* dependency just to typecheck `browse`/`clone`/`favorite`/`report`.
|
|
3816
|
+
*/
|
|
3817
|
+
/**
|
|
3818
|
+
* Community — browse, favorite, clone, and report shared characters,
|
|
3819
|
+
* locations, and objects.
|
|
3820
|
+
*
|
|
3821
|
+
* Publishing is intentionally NOT exposed here: it is admin-only via the
|
|
3822
|
+
* editor, and the publish route rejects personal access tokens (which is what
|
|
3823
|
+
* the SDK uses).
|
|
3824
|
+
*/
|
|
3825
|
+
declare class CommunityResource {
|
|
3826
|
+
private client;
|
|
3827
|
+
constructor(client: NodaroClient);
|
|
3828
|
+
/**
|
|
3829
|
+
* `GET /v1/community/browse` → a page of public listings plus a `nextCursor`.
|
|
3830
|
+
* Pass the returned `nextCursor` back as `cursor` to fetch the next page.
|
|
3831
|
+
*/
|
|
3832
|
+
browse(params?: BrowseCommunityParams): Promise<BrowseCommunityResult>;
|
|
3833
|
+
/** `GET /v1/community/detail/:slug` → a single listing by its slug. */
|
|
3834
|
+
get(slug: string): Promise<{
|
|
3835
|
+
data: CommunityCard;
|
|
3836
|
+
}>;
|
|
3837
|
+
/**
|
|
3838
|
+
* `GET /v1/community/detail/:slug/full` → the full read-only detail (card
|
|
3839
|
+
* identity + the stored public snapshot). Like {@link get}, but includes the
|
|
3840
|
+
* snapshot asset/voice/text blob needed to render the full cross-user view.
|
|
3841
|
+
*/
|
|
3842
|
+
getFull(slug: string): Promise<{
|
|
3843
|
+
data: CommunityFullDetail;
|
|
3844
|
+
}>;
|
|
3845
|
+
/** `GET /v1/community/favorites` → the listings you've favorited. */
|
|
3846
|
+
favorites(): Promise<{
|
|
3847
|
+
data: CommunityCard[];
|
|
3848
|
+
}>;
|
|
3849
|
+
/**
|
|
3850
|
+
* `POST /v1/community/listings/:id/clone` → copy a listing into your library.
|
|
3851
|
+
* Returns the new asset's `entityType` and `id`. Requires the `assets:write`
|
|
3852
|
+
* scope when called with an OAuth app token.
|
|
3853
|
+
*/
|
|
3854
|
+
clone(id: string, entityType: CommunityEntityType): Promise<CloneListingResult>;
|
|
3855
|
+
/**
|
|
3856
|
+
* `POST /v1/community/listings/:id/favorite` → toggle a favorite. Returns the
|
|
3857
|
+
* resulting `favorited` state (`true` after adding, `false` after removing).
|
|
3858
|
+
*/
|
|
3859
|
+
favorite(id: string): Promise<FavoriteListingResult>;
|
|
3860
|
+
/**
|
|
3861
|
+
* `POST /v1/community/listings/:id/report` → flag a listing for moderation.
|
|
3862
|
+
* `reason` must be one of {@link CommunityReportReason}.
|
|
3863
|
+
*/
|
|
3864
|
+
report(id: string, reason: CommunityReportReason): Promise<ReportListingResult>;
|
|
3865
|
+
/**
|
|
3866
|
+
* `POST /v1/admin/community/:entityType/:id/publish` → share one of YOUR
|
|
3867
|
+
* entities to the community, returning the new listing's `slug` + `id`.
|
|
3868
|
+
*
|
|
3869
|
+
* **Requires an admin token** (the route is `requireAdmin`) AND the caller
|
|
3870
|
+
* must own the source entity. Personal/OAuth tokens without admin role get a
|
|
3871
|
+
* 401. For `character` listings, `params.likenessAttestation` must be `true`.
|
|
3872
|
+
*/
|
|
3873
|
+
publish(entityType: CommunityEntityType, entityId: string, params: PublishListingParams): Promise<PublishListingResult>;
|
|
3874
|
+
/**
|
|
3875
|
+
* `DELETE /v1/admin/community/listings/:id` → unshare (deactivate) a listing
|
|
3876
|
+
* you published. **Requires an admin token** (the route is `requireAdmin`).
|
|
3877
|
+
*/
|
|
3878
|
+
unpublish(listingId: string): Promise<{
|
|
3879
|
+
ok: boolean;
|
|
3880
|
+
}>;
|
|
3881
|
+
/**
|
|
3882
|
+
* `GET /v1/admin/community/by-source/:entityType/:sourceId` → look up YOUR
|
|
3883
|
+
* existing listing (if any) for a source entity. Returns `{ data: null }`
|
|
3884
|
+
* when the entity hasn't been shared. **Requires an admin token** (the route
|
|
3885
|
+
* is `requireAdmin`); only returns listings created by the caller.
|
|
3886
|
+
*/
|
|
3887
|
+
sharedListing(entityType: CommunityEntityType, sourceId: string): Promise<{
|
|
3888
|
+
data: SharedListing | null;
|
|
3889
|
+
}>;
|
|
3890
|
+
}
|
|
3891
|
+
|
|
3892
|
+
interface ClientOptions {
|
|
3893
|
+
/** Backend base URL, e.g. "https://nodaro.example.com" or empty string for same-origin. */
|
|
3894
|
+
baseUrl: string;
|
|
3895
|
+
/** Auth provider. Use StaticTokenAuth, supabaseAuth, or CallbackAuth. */
|
|
3896
|
+
auth: Auth;
|
|
3897
|
+
/** Optional fetch override (for tests or custom transports). */
|
|
3898
|
+
fetch?: typeof fetch;
|
|
3899
|
+
/** Default request timeout in ms. Default 60s. */
|
|
3900
|
+
timeoutMs?: number;
|
|
3901
|
+
}
|
|
3902
|
+
interface RequestOptions {
|
|
3903
|
+
body?: unknown;
|
|
3904
|
+
query?: Record<string, string | number | boolean | undefined>;
|
|
3905
|
+
headers?: Record<string, string>;
|
|
3906
|
+
signal?: AbortSignal;
|
|
3907
|
+
}
|
|
3908
|
+
/**
|
|
3909
|
+
* The authenticated user's canonical identity (`GET /v1/me`). A token-
|
|
3910
|
+
* introspection primitive: any valid bearer token (first-party Supabase JWT or
|
|
3911
|
+
* a developer-app OAuth token) resolves to its owner's identity. Mirrors the
|
|
3912
|
+
* `profiles` identity columns server-side — the route is the source of truth.
|
|
3913
|
+
*/
|
|
3914
|
+
interface UserIdentity {
|
|
3915
|
+
/** Nodaro user id (= the Supabase auth user id). */
|
|
3916
|
+
readonly id: string;
|
|
3917
|
+
readonly email: string;
|
|
3918
|
+
/** Human-readable display name (from `profiles.full_name`); `null` if unset. */
|
|
3919
|
+
readonly displayName: string | null;
|
|
3920
|
+
/** Avatar URL; `null` if unset. */
|
|
3921
|
+
readonly avatarUrl: string | null;
|
|
3922
|
+
/** Subscription tier (e.g. "free", "pro"). */
|
|
3923
|
+
readonly tier: string;
|
|
3924
|
+
}
|
|
3925
|
+
declare class NodaroClient {
|
|
3926
|
+
readonly baseUrl: string;
|
|
3927
|
+
readonly auth: Auth;
|
|
3928
|
+
readonly timeoutMs: number;
|
|
3929
|
+
private readonly fetchOverride;
|
|
3930
|
+
/**
|
|
3931
|
+
* Resolved lazily so consumers can swap `globalThis.fetch` after the
|
|
3932
|
+
* client has been constructed (e.g. test mocks). Always rebound to the
|
|
3933
|
+
* global object — native fetch throws "Illegal invocation" when its
|
|
3934
|
+
* `this` is anything else.
|
|
3935
|
+
*/
|
|
3936
|
+
get fetch(): typeof fetch;
|
|
3937
|
+
readonly workflows: WorkflowsResource;
|
|
3938
|
+
readonly projects: ProjectsResource;
|
|
3939
|
+
readonly jobs: JobsResource;
|
|
3940
|
+
readonly executions: ExecutionsResource;
|
|
3941
|
+
readonly nodes: NodesResource;
|
|
3942
|
+
readonly developerApps: DeveloperAppsResource;
|
|
3943
|
+
readonly oauth: OAuthResource;
|
|
3944
|
+
readonly apps: AppsResource;
|
|
3945
|
+
readonly characters: CharactersResource;
|
|
3946
|
+
readonly locations: LocationsResource;
|
|
3947
|
+
readonly objects: ObjectsResource;
|
|
3948
|
+
readonly creatures: CreaturesResource;
|
|
3949
|
+
readonly pipelines: PipelinesResource;
|
|
3950
|
+
readonly reduce: ReduceResource;
|
|
3951
|
+
readonly promptHelper: PromptHelperResource;
|
|
3952
|
+
readonly voices: VoicesResource;
|
|
3953
|
+
readonly credits: CreditsResource;
|
|
3954
|
+
readonly uploads: UploadsResource;
|
|
3955
|
+
readonly library: LibraryResource;
|
|
3956
|
+
readonly presets: PresetsResource;
|
|
3957
|
+
readonly pickerCatalogs: PickerCatalogsResource;
|
|
3958
|
+
readonly community: CommunityResource;
|
|
3959
|
+
constructor(opts: ClientOptions);
|
|
3960
|
+
request<T>(method: string, path: string, options?: RequestOptions): Promise<T>;
|
|
3961
|
+
/**
|
|
3962
|
+
* `GET /v1/me` → the authenticated user's identity (see {@link UserIdentity}).
|
|
3963
|
+
* Unwraps the `{ data }` envelope. Throws `UnauthorizedError` (401) when the
|
|
3964
|
+
* token is missing/invalid, and the SDK's other typed errors as usual.
|
|
3965
|
+
*/
|
|
3966
|
+
me(): Promise<UserIdentity>;
|
|
3967
|
+
private buildUrl;
|
|
3968
|
+
}
|
|
3969
|
+
/** Factory function — preferred entry point. */
|
|
3970
|
+
declare function createClient(opts: ClientOptions): NodaroClient;
|
|
3971
|
+
|
|
3972
|
+
declare class NodaroError extends Error {
|
|
3973
|
+
readonly code: string;
|
|
3974
|
+
readonly status: number;
|
|
3975
|
+
constructor(message: string, code: string, status: number);
|
|
3976
|
+
}
|
|
3977
|
+
declare class UnauthorizedError extends NodaroError {
|
|
3978
|
+
constructor(message?: string);
|
|
3979
|
+
}
|
|
3980
|
+
declare class ForbiddenError extends NodaroError {
|
|
3981
|
+
readonly missingScope?: string | undefined;
|
|
3982
|
+
constructor(message?: string, missingScope?: string | undefined);
|
|
3983
|
+
}
|
|
3984
|
+
declare class NotFoundError extends NodaroError {
|
|
3985
|
+
constructor(message?: string);
|
|
3986
|
+
}
|
|
3987
|
+
declare class RateLimitedError extends NodaroError {
|
|
3988
|
+
constructor(message?: string);
|
|
3989
|
+
}
|
|
3990
|
+
declare class InsufficientCreditsError extends NodaroError {
|
|
3991
|
+
readonly required?: number | undefined;
|
|
3992
|
+
readonly available?: number | undefined;
|
|
3993
|
+
constructor(message?: string, required?: number | undefined, available?: number | undefined);
|
|
3994
|
+
}
|
|
3995
|
+
declare class StorageExceededError extends NodaroError {
|
|
3996
|
+
readonly limitBytes?: number | undefined;
|
|
3997
|
+
constructor(message?: string, limitBytes?: number | undefined);
|
|
3998
|
+
}
|
|
3999
|
+
/**
|
|
4000
|
+
* A job reached a terminal `failed`/`cancelled` status while being awaited by
|
|
4001
|
+
* `nodes.runAndWait` / `nodes.runMany`. Not an HTTP-level error (the polls
|
|
4002
|
+
* themselves succeeded), so `status` is 0 — distinguish it by type/`code`.
|
|
4003
|
+
* Carries the job's own `error_message` (as the message) and `jobId`.
|
|
4004
|
+
*/
|
|
4005
|
+
declare class JobFailedError extends NodaroError {
|
|
4006
|
+
readonly jobId: string;
|
|
4007
|
+
/** The terminal status that triggered the failure (`failed` | `cancelled`). */
|
|
4008
|
+
readonly jobStatus: "failed" | "cancelled";
|
|
4009
|
+
constructor(message: string, jobId: string,
|
|
4010
|
+
/** The terminal status that triggered the failure (`failed` | `cancelled`). */
|
|
4011
|
+
jobStatus?: "failed" | "cancelled");
|
|
4012
|
+
}
|
|
4013
|
+
/**
|
|
4014
|
+
* `nodes.runAndWait` polled past its `maxMs` deadline without the job reaching
|
|
4015
|
+
* a terminal status. Not an HTTP error — `status` is 0; catch by type/`code`.
|
|
4016
|
+
*/
|
|
4017
|
+
declare class JobTimeoutError extends NodaroError {
|
|
4018
|
+
readonly jobId: string;
|
|
4019
|
+
/** The wall-clock deadline (ms) that was exceeded. */
|
|
4020
|
+
readonly timeoutMs: number;
|
|
4021
|
+
constructor(message: string, jobId: string,
|
|
4022
|
+
/** The wall-clock deadline (ms) that was exceeded. */
|
|
4023
|
+
timeoutMs: number);
|
|
4024
|
+
}
|
|
4025
|
+
/**
|
|
4026
|
+
* The caller's `AbortSignal` fired while `nodes.runAndWait` was polling (or it
|
|
4027
|
+
* was already aborted on entry). Polling stops and this rejects. Not an HTTP
|
|
4028
|
+
* error — `status` is 0; catch by type/`code`.
|
|
4029
|
+
*/
|
|
4030
|
+
declare class JobAbortedError extends NodaroError {
|
|
4031
|
+
readonly jobId?: string | undefined;
|
|
4032
|
+
constructor(message?: string, jobId?: string | undefined);
|
|
4033
|
+
}
|
|
4034
|
+
interface ApiErrorBody {
|
|
4035
|
+
error?: {
|
|
4036
|
+
code?: string;
|
|
4037
|
+
message?: string;
|
|
4038
|
+
missingScope?: string;
|
|
4039
|
+
required?: number;
|
|
4040
|
+
available?: number;
|
|
4041
|
+
limitBytes?: number;
|
|
4042
|
+
[key: string]: unknown;
|
|
4043
|
+
};
|
|
4044
|
+
}
|
|
4045
|
+
declare function throwFromResponse(status: number, body: ApiErrorBody): never;
|
|
4046
|
+
|
|
4047
|
+
export { type AccessTokenResponse, type AnalyzeInput, type AnalyzeResult, type AppRun, type AppRunResult, type ApplyChatProposalResult, type ApproveCreatureMainImageResult, type ApproveMainImageResult, type ApproveObjectMainImageResult, type ApprovePortraitResult, AppsResource, type Auth, type BranchPipelineInput, type BranchPipelineResult, CREATURE_ASSET_TYPES, CallbackAuth, type CancelExecutionParams, type CancelJobResult, type Character, type CharacterDetail, type CharacterUsage, CharactersResource, type ChatStageResult, type ChatTurn, type ClientOptions, CommunityResource, type CreateCreatureInput, type CreateDeveloperAppInput, type CreateDeveloperAppResult, type CreateLocationInput, type CreateObjectInput, type CreateProjectInput, type CreateWorkflowInput, type Creature, type CreatureAssetType, type CreatureDetail, type CreatureReferencePhoto, type CreatureReferencePhotoKind, CreaturesResource, CreditsResource, type DeleteAppRunResult, type DeveloperApp, type DeveloperAppScope, type DeveloperAppStatus, DeveloperAppsResource, type DuplicateCharacterInput, type EnhanceInput, type ExchangeCodeInput, type ExecutionStatus, type ExecutionTriggerType, ExecutionsResource, type FactoryPresetsResult, ForbiddenError, type GenerateAssetInput, type GenerateCharacterInput, type GenerateCharacterResult, type GenerateCreatureAssetInput, type GenerateCreatureAssetResult, type GenerateCreatureInput, type GenerateCreatureMotionInput, type GenerateCreatureMotionResult, type GenerateCreatureResult, type GenerateImageParams, type GenerateInput, type GenerateLocationAssetInput, type GenerateLocationInput, type GenerateLocationResult, type GenerateMotionInput, type GenerateObjectAssetInput, type GenerateObjectAssetResult, type GenerateObjectInput, type GenerateObjectMotionInput, type GenerateObjectMotionResult, type GenerateObjectResult, type GenerateSurroundContinuationInput, type GenerateVideoParams, type GetPickerCatalogOptions, InsufficientCreditsError, type Job, JobAbortedError, JobFailedError, type JobStatus, type JobStatusResult, JobTimeoutError, JobsResource, type LibraryAsset, LibraryResource, type ListAppRunsParams, type ListAppsParams, type ListAppsResult, type ListCharactersParams, type ListCreaturesParams, type ListExecutionsForWorkflowParams, type ListExecutionsPage, type ListLibraryParams, type ListLibraryResult, type ListLocationsParams, type ListObjectsParams, type ListWorkflowsParams, type Location, type LocationDetail, type LocationReferencePhoto, type LocationReferencePhotoKind, LocationsResource, type ModelCostsResult, NodaroClient, NodaroError, type NodeCategory, type NodeDescriptor, type NodeExecutionState, type NodeInputField, type NodeInputSchema, type NodeJobOutput, type NodePreset, type NodePresetGroup, NodesResource, NotFoundError, type OAuthAppInfo, OAuthResource, type Object$1 as Object, type ObjectCategory, type ObjectDetail, type ObjectReferencePhoto, type ObjectReferencePhotoKind, ObjectsResource, type OutputType, type PickerCatalog, type PickerCatalogSummary, PickerCatalogsResource, type PickerDimension, type PickerOption, PipelinesResource, PresetsResource, type Project, ProjectsResource, PromptHelperResource, type PromptResult, type PublishedApp, type PublishedAppDetail, RateLimitedError, type RecaptionCreatureResult, type RecaptionLocationResult, type RecaptionObjectResult, type RecaptionResult, type ReduceInput, ReduceResource, type ReduceResult, type ReferencePhoto, type ReferencePhotoKind, type RotateSecretResult, type RunAndWaitOptions, type RunManyResult, type RunNodeResult, type RunWorkflowParams, type RunWorkflowResult, StaticTokenAuth, StorageExceededError, type StructuredReferenceParams, UnauthorizedError, type UpdateCreatureInput, type UpdateCreatureResult, type UpdateDeveloperAppInput, type UpdateLocationInput, type UpdateLocationResult, type UpdateObjectInput, type UpdateObjectResult, type UpdateProjectInput, type UpdateWorkflowInput, type UploadResult, UploadsResource, type UpsertCharacterInput, type UpsertCharacterResult, type UpsertCreatureInput, type UpsertCreatureResult, type UpsertObjectInput, type UpsertObjectResult, type UserBalance, type UserIdentity, type VoiceChangerProInput, type VoiceChangerProVoice, VoicesResource, type Workflow, type WorkflowExecution, type WorkflowExecutionSummary, WorkflowsResource, buildPersonSeedPrompt, createClient, supabaseAuth, throwFromResponse };
|