@melius-ai/cli 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -265,21 +265,29 @@ Requires the Pro or Enterprise plan.
265
265
  | `mel asset folders` | List the library's folders (`id`, `name`, `path`, `fileCount`) so you can resolve a folder name to an id for `--folder-id` |
266
266
  | `mel asset place <canvasId> <assetIds...>` | Place existing library assets onto a canvas by id — references the **original** asset (no duplicate) |
267
267
 
268
- `mel asset search` searches by meaning (semantic), with exact filename/title matches ranked first, so query with a filename or a description of the content. It returns `{ query, total, results: [{ assetId, filename, fileType, previewUrl, ... }] }`. Filter with `--file-type <image|video|audio|pdf|text>` and `--limit <1-20>`; page deeper with `--offset <n>` (skip the first N matches — page until the running count reaches `total`); scope to one canvas with `--canvas-id` (omit to search the whole team library).
268
+ `mel asset search` searches by meaning (semantic), with exact filename/title matches ranked first, so query with a filename or a description of the content. Pass an exact filename verbatim, including its extension. It returns `{ query, total, results: [{ assetId, filename, fileType, previewUrl, ... }] }`. Filter by origin with `--source <upload|generated>`, by media type with `--file-type <image|video|audio|pdf|text>`, and by page size with `--limit <1-20>`; page deeper with `--offset <n>` (skip the first N matches — page until the running count reaches `total`); scope to one canvas with `--canvas-id` (omit to search the whole team library).
269
269
 
270
270
  Scope to a **folder** with `--folder-id` (from `mel asset folders`): with a query it filters within that folder and its subfolders; **omit the query** to bring in the _whole_ folder (every asset in it). `mel asset folders` returns each folder's root-relative `path` (e.g. `Brand / Logos`) to disambiguate same-named folders.
271
271
 
272
+ Filter by **tag** with `--tags <names>` (comma-separated, case-insensitive): assets carrying **all** the named tags (AND). Combine with a query, or use `--tags` alone (or with `--folder-id`) to bring in every asset carrying them. An unknown tag name matches nothing.
273
+
272
274
  `mel asset place` creates a file node per asset (writes to CRDT, no re-upload) and returns `{ placed: [{ assetId, nodeId, mediaType }] }`. Wire the returned `nodeId`s into generations with `mel edge create`. This is the correct way to reuse an existing library asset — do **not** re-upload it.
273
275
 
274
276
  ```bash
275
277
  # Find an asset, then bring it onto a canvas and wire it
276
278
  mel asset search "black jacket shot" --file-type image
279
+ mel asset search "Product_Shot_05.PNG" --source upload
280
+ mel asset search "shark close-up" --source generated
277
281
  mel asset place <canvasId> <assetId1> <assetId2>
278
282
 
279
283
  # Bring in a whole folder: resolve the folder, then search it with no query
280
284
  mel asset folders # → [{ id, name, path, fileCount }]
281
285
  mel asset search --folder-id <folderId> # every asset in the folder
282
286
  mel asset search "shark" --folder-id <folderId> # or filter within it
287
+
288
+ # Filter by tag (AND across tags)
289
+ mel asset search "logo" --tags brand # tagged 'brand'
290
+ mel asset search --tags brand,hero # every asset with BOTH tags
283
291
  ```
284
292
 
285
293
  ### Shell completion
package/dist/bin/mel.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  printUpgradeNoticeIfNeeded,
8
8
  withDefaultHelp,
9
9
  writeError
10
- } from "../chunk-5QT6WMID.js";
10
+ } from "../chunk-24FW76XZ.js";
11
11
 
12
12
  // bin/mel.ts
13
13
  var program = createProgram();
@@ -7736,6 +7736,7 @@ var MAX_INPUT_VIDEOS = 3;
7736
7736
  var MAX_INPUT_DOCUMENTS = 5;
7737
7737
  var MAX_AGENT_MESSAGE_CONTENT_LENGTH = 5e4;
7738
7738
  var MAX_SEED = 2147483647;
7739
+ var MAX_VARIATIONS = 10;
7739
7740
 
7740
7741
  // ../api-contract/src/common.ts
7741
7742
  var MessageResponseSchema = external_exports.object({ message: external_exports.string() });
@@ -7911,6 +7912,24 @@ var FileTypeSchema = external_exports.enum([
7911
7912
  "pdf",
7912
7913
  "text"
7913
7914
  ]);
7915
+ var AssetShareModeSchema = external_exports.enum(["team", "anyone"]);
7916
+ var TAG_COLOR_PALETTE = [
7917
+ "hsl(189deg, 72%, 46%)",
7918
+ "hsl(280deg, 60%, 50%)",
7919
+ "hsl(24deg, 76%, 46%)",
7920
+ "hsl(142deg, 58%, 40%)",
7921
+ "hsl(330deg, 64%, 46%)",
7922
+ "hsl(45deg, 68%, 42%)",
7923
+ "hsl(210deg, 66%, 44%)",
7924
+ "hsl(168deg, 55%, 38%)"
7925
+ ];
7926
+ var TagColorSchema = external_exports.enum(TAG_COLOR_PALETTE);
7927
+ var MAX_ASSET_TAG_NAME_LENGTH = 100;
7928
+ var FileTagSchema = external_exports.object({
7929
+ id: external_exports.string().uuid(),
7930
+ name: external_exports.string(),
7931
+ color: external_exports.string()
7932
+ });
7914
7933
  var FileCanvasBacklinkSchema = external_exports.object({
7915
7934
  canvasId: external_exports.string().uuid(),
7916
7935
  projectId: external_exports.string().uuid(),
@@ -7939,6 +7958,9 @@ var FileCardSchema = external_exports.object({
7939
7958
  * The "Open canvas" CTA opens the first directly, or — when there is more
7940
7959
  * than one — prompts the user to pick which to open. */
7941
7960
  canvases: external_exports.array(FileCanvasBacklinkSchema),
7961
+ // The tags assigned to this file (unordered). Always present — empty when
7962
+ // untagged — so the grid can render chips without a per-card fetch.
7963
+ tags: external_exports.array(FileTagSchema),
7942
7964
  // The canvas node whose currently-active version produced this generated
7943
7965
  // asset. The Files grid shows only active-version outputs, so each
7944
7966
  // generated card maps to exactly one such node; this lets the viewer load
@@ -8020,6 +8042,9 @@ var BulkDownloadFilesResponseSchema = external_exports.object({
8020
8042
  var BulkHideFilesBodySchema = external_exports.object({
8021
8043
  assetIds: BulkFileAssetIdsSchema
8022
8044
  });
8045
+ var BulkDeleteFilesBodySchema = external_exports.object({
8046
+ assetIds: BulkFileAssetIdsSchema
8047
+ });
8023
8048
  var BulkUnhideFilesBodySchema = external_exports.object({
8024
8049
  assetIds: BulkFileAssetIdsSchema
8025
8050
  });
@@ -8049,6 +8074,13 @@ var ListFilesQuerySchema = external_exports.object({
8049
8074
  createdByUserId: external_exports.string().uuid().optional(),
8050
8075
  sort: FileSortSchema.default("created_desc"),
8051
8076
  folderId: external_exports.union([external_exports.string().uuid(), external_exports.literal("root")]).optional(),
8077
+ // Filter to files carrying ALL of these tag ids (AND — narrows the set).
8078
+ // Comma-separated in the query string (e.g. `?tagIds=<id1>,<id2>`) because
8079
+ // this GET has no array-query support; absent means no tag filter. Each id
8080
+ // is validated as a uuid after splitting.
8081
+ tagIds: external_exports.string().optional().transform(
8082
+ (value) => value ? value.split(",").map((id) => id.trim()).filter(Boolean) : void 0
8083
+ ).pipe(external_exports.array(external_exports.string().uuid()).optional()),
8052
8084
  // The "Hidden" view: lists files hidden from Files (across all folders)
8053
8085
  // instead of the normal visible listing. Coerced from the query string so
8054
8086
  // `?hidden=true` works; absent / "false" both resolve to `false`.
@@ -8083,19 +8115,35 @@ var ListFilesResponseSchema = external_exports.object({
8083
8115
  cursor: external_exports.string().nullable().optional(),
8084
8116
  hasMore: external_exports.boolean().optional()
8085
8117
  });
8118
+ var FileTabCountsQuerySchema = external_exports.object({
8119
+ q: external_exports.string().optional(),
8120
+ folderId: external_exports.union([external_exports.string().uuid(), external_exports.literal("root")]).optional()
8121
+ });
8122
+ var FileTabCountsResponseSchema = external_exports.object({
8123
+ // "All assets" — always uploads + generations (a file is one or the other).
8124
+ all: external_exports.number().int(),
8125
+ uploads: external_exports.number().int(),
8126
+ generations: external_exports.number().int(),
8127
+ favorites: external_exports.number().int()
8128
+ });
8086
8129
  var AGENT_ASSET_SEARCH_PAGE_SIZE = 20;
8087
8130
  var AgentAssetSearchQuerySchema = external_exports.object({
8088
8131
  query: external_exports.string().trim().min(1).max(500).optional().describe(
8089
- 'The semantic search query \u2014 a filename or a description of the asset (e.g. "red logo", "the shark photo"). Provide it to FIND specific assets. OMIT it entirely to bring in EVERY asset from a folder: when the member says "bring in all my assets from the X folder" / "add everything in X to the canvas", resolve the folder with list_asset_folders and call asset_search with just that `folderId` and NO query. Never invent a query when the member asked for everything in a folder \u2014 a query filters and will drop assets they wanted. Omitting the query requires a `folderId`.'
8132
+ 'The semantic search query \u2014 an exact filename or a description of the asset (e.g. "Product_Shot_05.png", "red logo", "the shark photo"). When the member gives an exact filename, pass it verbatim, including its extension; exact matches rank first. Provide a query to FIND specific assets. OMIT it entirely to bring in EVERY asset from a folder: when the member says "bring in all my assets from the X folder" / "add everything in X to the canvas", resolve the folder with list_asset_folders and call asset_search with just that `folderId` and NO query. Never invent a query when the member asked for everything in a folder \u2014 a query filters and will drop assets they wanted. Omitting the query requires a `folderId`.'
8090
8133
  ),
8091
8134
  limit: external_exports.coerce.number().int().min(1).max(AGENT_ASSET_SEARCH_PAGE_SIZE).default(AGENT_ASSET_SEARCH_PAGE_SIZE),
8092
- source: FileSourceSchema.optional(),
8135
+ source: FileSourceSchema.optional().describe(
8136
+ "Optional. Filter by where the asset came from: `upload` for files uploaded by a member, or `generated` for media created in Melius. Set this only when the member explicitly asks for uploads or generations; omit it to search both."
8137
+ ),
8093
8138
  fileType: FileTypeSchema.optional(),
8094
8139
  canvasId: external_exports.string().uuid().optional().describe(
8095
8140
  'Optional. Omit to search the whole team library \u2014 this is the default and what nearly every request wants (e.g. "use the asset I have"). Only set this to scope to a DIFFERENT canvas the member explicitly names ("search my other canvas"), resolving the id from canvas_list. Do NOT pass the canvas the chat is currently on (its assets are already visible via canvas_content, and scoping there hides whole-library matches), and never guess an id.'
8096
8141
  ),
8097
8142
  folderId: external_exports.string().uuid().optional().describe(
8098
- 'Optional. Scope to one Files folder AND its subfolders. Omit to search the whole team library \u2014 the default, and what nearly every request wants. Set it when the member names a specific folder \u2014 either to FILTER within it (pass `folderId` + `query`, e.g. "find the shark shot in my ocean folder") or to bring in EVERYTHING in it (pass `folderId` and OMIT `query`, e.g. "add all my assets from the ocean folder"). Resolve the id with list_asset_folders (match by name; if several plausibly match, ask which one); never guess a folder id.'
8143
+ 'Optional. Scope to one Files folder AND its subfolders. Omit to search the whole team library \u2014 the default, and what nearly every request wants. Set it when the member names a specific folder \u2014 either to FILTER within it (pass `folderId` + `query`, e.g. "find the shark shot in my ocean folder") or to bring in EVERYTHING in it (pass `folderId` and OMIT `query`, e.g. "add all my assets from the ocean folder"). Resolve the id with list_asset_folders: prefer a case-insensitive exact name or path before considering partial matches; if several exact or partial matches remain, ask which one. Never guess a folder id.'
8144
+ ),
8145
+ tags: external_exports.string().trim().optional().describe(
8146
+ 'Optional. Filter to assets carrying ALL of these tags (AND). Pass a tag name ONLY when the member explicitly asks to filter by a tag (e.g. "find my assets tagged brand", "my hero tag") \u2014 never infer one from ordinary words in their request. Comma-separated (e.g. "brand" or "brand,hero"), case-insensitive. Combine with `query` to search within tagged assets, or omit `query` (with tags and/or a folderId) to bring in every asset carrying them. An unknown or misspelled tag name matches nothing, so pass only tags the member actually named.'
8099
8147
  ),
8100
8148
  showInThread: external_exports.boolean().optional().describe(
8101
8149
  'Slack only. Set true when the member only wants to SEE the matched asset(s) \u2014 "find X and show it to me", "do I have Y?" \u2014 and is NOT asking to place, edit, or build on them. Skips the selection card and posts every match straight into the Slack thread as image files. Leave unset (the default) whenever the member wants to USE an asset on the canvas, so the selection card renders and a canvas gets bound.'
@@ -8125,7 +8173,10 @@ var AgentAssetSearchResponseSchema = external_exports.object({
8125
8173
  folderId: external_exports.string().uuid().optional(),
8126
8174
  canvasId: external_exports.string().uuid().optional(),
8127
8175
  source: FileSourceSchema.optional(),
8128
- fileType: FileTypeSchema.optional()
8176
+ fileType: FileTypeSchema.optional(),
8177
+ // Echoed comma-separated tag filter, so the picker's "More results" re-pages
8178
+ // with the same tags.
8179
+ tags: external_exports.string().optional()
8129
8180
  });
8130
8181
  var AgentListAssetFoldersQuerySchema = external_exports.object({});
8131
8182
  var AgentAssetFolderSchema = external_exports.object({
@@ -8169,6 +8220,46 @@ var EmbeddingStorageUsageResponseSchema = external_exports.object({
8169
8220
  usedBytes: external_exports.number(),
8170
8221
  limitBytes: external_exports.number()
8171
8222
  });
8223
+ var AssetTagResponseSchema = external_exports.object({
8224
+ id: external_exports.string().uuid(),
8225
+ teamId: external_exports.string().uuid(),
8226
+ name: external_exports.string(),
8227
+ color: external_exports.string(),
8228
+ createdByUserId: external_exports.string().uuid().nullable(),
8229
+ createdByName: external_exports.string().nullable(),
8230
+ createdAt: external_exports.string().datetime(),
8231
+ updatedAt: external_exports.string().datetime(),
8232
+ // How many of the team's assets carry this tag. Present in listAssetTags;
8233
+ // omitted on single-tag create/update responses (which don't recount).
8234
+ assetCount: external_exports.number().int().nonnegative().optional()
8235
+ });
8236
+ var AssetTagListResponseSchema = external_exports.object({
8237
+ data: external_exports.array(AssetTagResponseSchema)
8238
+ });
8239
+ var AssetTagIdParamsSchema = external_exports.object({
8240
+ tagId: external_exports.string().uuid()
8241
+ });
8242
+ var FileAssetTagParamsSchema = external_exports.object({
8243
+ assetId: external_exports.string().uuid(),
8244
+ tagId: external_exports.string().uuid()
8245
+ });
8246
+ var CreateAssetTagBodySchema = external_exports.object({
8247
+ name: external_exports.string().trim().min(1).max(MAX_ASSET_TAG_NAME_LENGTH),
8248
+ color: TagColorSchema
8249
+ });
8250
+ var UpdateAssetTagBodySchema = external_exports.object({
8251
+ name: external_exports.string().trim().min(1).max(MAX_ASSET_TAG_NAME_LENGTH).optional(),
8252
+ color: TagColorSchema.optional()
8253
+ }).refine((body) => body.name !== void 0 || body.color !== void 0, {
8254
+ message: "Provide a name or color to update"
8255
+ });
8256
+ var AssetTagAssignmentBodySchema = external_exports.object({
8257
+ tagId: external_exports.string().uuid()
8258
+ });
8259
+ var BulkAssetTagBodySchema = external_exports.object({
8260
+ assetIds: BulkFileAssetIdsSchema,
8261
+ tagId: external_exports.string().uuid()
8262
+ });
8172
8263
  var ErrorBodySchema = MessageResponseSchema;
8173
8264
  var filesContract = c2.router({
8174
8265
  createFileFolder: {
@@ -8215,6 +8306,97 @@ var filesContract = c2.router({
8215
8306
  },
8216
8307
  summary: "Delete a Files folder"
8217
8308
  },
8309
+ listAssetTags: {
8310
+ method: "GET",
8311
+ path: "/files/tags",
8312
+ responses: {
8313
+ 200: AssetTagListResponseSchema,
8314
+ 403: ErrorBodySchema
8315
+ },
8316
+ summary: "List team asset tags"
8317
+ },
8318
+ createAssetTag: {
8319
+ method: "POST",
8320
+ path: "/files/tags",
8321
+ body: CreateAssetTagBodySchema,
8322
+ responses: {
8323
+ 201: AssetTagResponseSchema,
8324
+ 403: ErrorBodySchema,
8325
+ 409: ErrorBodySchema
8326
+ },
8327
+ summary: "Create an asset tag"
8328
+ },
8329
+ updateAssetTag: {
8330
+ method: "PATCH",
8331
+ path: "/files/tags/:tagId",
8332
+ pathParams: AssetTagIdParamsSchema,
8333
+ body: UpdateAssetTagBodySchema,
8334
+ responses: {
8335
+ 200: AssetTagResponseSchema,
8336
+ 403: ErrorBodySchema,
8337
+ 404: ErrorBodySchema,
8338
+ 409: ErrorBodySchema
8339
+ },
8340
+ summary: "Rename or recolor an asset tag"
8341
+ },
8342
+ deleteAssetTag: {
8343
+ method: "DELETE",
8344
+ path: "/files/tags/:tagId",
8345
+ pathParams: AssetTagIdParamsSchema,
8346
+ body: null,
8347
+ responses: {
8348
+ 204: c2.noBody(),
8349
+ 403: ErrorBodySchema,
8350
+ 404: ErrorBodySchema
8351
+ },
8352
+ summary: "Delete an asset tag"
8353
+ },
8354
+ addAssetTag: {
8355
+ method: "POST",
8356
+ path: "/files/:assetId/tags",
8357
+ pathParams: FileAssetIdParamsSchema,
8358
+ body: AssetTagAssignmentBodySchema,
8359
+ responses: {
8360
+ 200: FileCardSchema,
8361
+ 403: ErrorBodySchema,
8362
+ 404: ErrorBodySchema
8363
+ },
8364
+ summary: "Add a tag to a file"
8365
+ },
8366
+ removeAssetTag: {
8367
+ method: "DELETE",
8368
+ path: "/files/:assetId/tags/:tagId",
8369
+ pathParams: FileAssetTagParamsSchema,
8370
+ body: null,
8371
+ responses: {
8372
+ 200: FileCardSchema,
8373
+ 403: ErrorBodySchema,
8374
+ 404: ErrorBodySchema
8375
+ },
8376
+ summary: "Remove a tag from a file"
8377
+ },
8378
+ bulkAddAssetTag: {
8379
+ method: "POST",
8380
+ path: "/files/bulk/tag",
8381
+ body: BulkAssetTagBodySchema,
8382
+ responses: {
8383
+ 200: BulkFileActionResponseSchema,
8384
+ 403: ErrorBodySchema,
8385
+ 404: ErrorBodySchema
8386
+ },
8387
+ summary: "Add a tag to files"
8388
+ },
8389
+ bulkRemoveAssetTag: {
8390
+ method: "POST",
8391
+ path: "/files/bulk/untag",
8392
+ body: BulkAssetTagBodySchema,
8393
+ responses: {
8394
+ 200: BulkFileActionResponseSchema,
8395
+ 403: ErrorBodySchema,
8396
+ 404: ErrorBodySchema
8397
+ },
8398
+ summary: "Remove a tag from files"
8399
+ },
8218
8400
  bulkMoveFiles: {
8219
8401
  method: "POST",
8220
8402
  path: "/files/bulk/move",
@@ -8260,6 +8442,17 @@ var filesContract = c2.router({
8260
8442
  },
8261
8443
  summary: "Hide files from Files"
8262
8444
  },
8445
+ bulkDeleteFiles: {
8446
+ method: "POST",
8447
+ path: "/files/bulk/delete",
8448
+ body: BulkDeleteFilesBodySchema,
8449
+ responses: {
8450
+ 200: BulkFileActionResponseSchema,
8451
+ 403: ErrorBodySchema,
8452
+ 404: ErrorBodySchema
8453
+ },
8454
+ summary: "Delete files from Files"
8455
+ },
8263
8456
  hideFile: {
8264
8457
  method: "POST",
8265
8458
  path: "/files/:assetId/hide",
@@ -8272,6 +8465,18 @@ var filesContract = c2.router({
8272
8465
  },
8273
8466
  summary: "Hide a file from Files"
8274
8467
  },
8468
+ deleteFile: {
8469
+ method: "DELETE",
8470
+ path: "/files/:assetId",
8471
+ pathParams: FileAssetIdParamsSchema,
8472
+ body: null,
8473
+ responses: {
8474
+ 204: c2.noBody(),
8475
+ 403: ErrorBodySchema,
8476
+ 404: ErrorBodySchema
8477
+ },
8478
+ summary: "Delete a file from Files"
8479
+ },
8275
8480
  bulkUnhideFiles: {
8276
8481
  method: "POST",
8277
8482
  path: "/files/bulk/unhide",
@@ -8305,6 +8510,16 @@ var filesContract = c2.router({
8305
8510
  },
8306
8511
  summary: "List team files"
8307
8512
  },
8513
+ getFileTabCounts: {
8514
+ method: "GET",
8515
+ path: "/files/tab-counts",
8516
+ query: FileTabCountsQuerySchema,
8517
+ responses: {
8518
+ 200: FileTabCountsResponseSchema,
8519
+ 403: ErrorBodySchema
8520
+ },
8521
+ summary: "Per-tab asset counts for the Assets page, scoped to the search"
8522
+ },
8308
8523
  searchAgentAssets: {
8309
8524
  method: "GET",
8310
8525
  path: "/files/agent-search",
@@ -8344,6 +8559,29 @@ var filesContract = c2.router({
8344
8559
  },
8345
8560
  summary: "Get a file's details with generation metadata"
8346
8561
  },
8562
+ getAssetShareMode: {
8563
+ method: "GET",
8564
+ path: "/files/:assetId/share-mode",
8565
+ pathParams: FileAssetIdParamsSchema,
8566
+ responses: {
8567
+ 200: external_exports.object({ shareMode: AssetShareModeSchema }),
8568
+ 403: ErrorBodySchema,
8569
+ 404: ErrorBodySchema
8570
+ },
8571
+ summary: "Get an asset's share mode"
8572
+ },
8573
+ updateAssetShareMode: {
8574
+ method: "PATCH",
8575
+ path: "/files/:assetId/share-mode",
8576
+ pathParams: FileAssetIdParamsSchema,
8577
+ body: external_exports.object({ shareMode: AssetShareModeSchema }),
8578
+ responses: {
8579
+ 200: external_exports.object({ shareMode: AssetShareModeSchema }),
8580
+ 403: ErrorBodySchema,
8581
+ 404: ErrorBodySchema
8582
+ },
8583
+ summary: "Set who can view an asset"
8584
+ },
8347
8585
  initFileUpload: {
8348
8586
  method: "POST",
8349
8587
  path: "/files/upload/init",
@@ -9040,38 +9278,44 @@ var AUTO_MODEL_REGISTRY = [
9040
9278
  {
9041
9279
  nodeType: "video",
9042
9280
  inputPattern: "text-only",
9043
- standard: { model: "kling-o3", variant: "text-to-video" },
9044
- fast: { model: "grok-imagine-video", variant: "text-to-video" }
9281
+ model: "kling-o3",
9282
+ variant: "text-to-video"
9045
9283
  },
9046
9284
  {
9047
9285
  nodeType: "video",
9048
9286
  inputPattern: "image-to-video",
9049
- standard: { model: "kling-o3", variant: "image-to-video" },
9050
- fast: { model: "wan-2.2", variant: "image-to-video" }
9287
+ model: "kling-o3",
9288
+ variant: "image-to-video"
9051
9289
  },
9052
9290
  {
9053
9291
  nodeType: "video",
9054
9292
  inputPattern: "first-last-frame-to-video",
9055
- standard: { model: "kling-o3", variant: "image-to-video" },
9056
- fast: { model: "kling-o3", variant: "image-to-video" }
9293
+ model: "kling-o3",
9294
+ variant: "image-to-video"
9057
9295
  },
9058
9296
  {
9059
9297
  nodeType: "video",
9060
9298
  inputPattern: "reference-to-video",
9061
- standard: { model: "kling-o3", variant: "reference-to-video" },
9062
- fast: { model: "kling-o3", variant: "reference-to-video" }
9299
+ model: "kling-o3",
9300
+ variant: "reference-to-video"
9063
9301
  },
9064
9302
  {
9065
9303
  nodeType: "video",
9066
9304
  inputPattern: "reference-with-frames-to-video",
9067
- standard: { model: "kling-o3", variant: "reference-to-video" },
9068
- fast: { model: "kling-o3", variant: "reference-to-video" }
9305
+ model: "kling-o3",
9306
+ variant: "reference-to-video"
9069
9307
  },
9070
9308
  {
9071
9309
  nodeType: "video",
9072
9310
  inputPattern: "video-with-audio",
9073
- standard: { model: "infinitalk", variant: "video-to-video" },
9074
- fast: { model: "infinitalk", variant: "video-to-video" }
9311
+ model: "infinitalk",
9312
+ variant: "video-to-video"
9313
+ },
9314
+ {
9315
+ nodeType: "video",
9316
+ inputPattern: "video-with-image-references",
9317
+ model: "kling-o3",
9318
+ variant: "video-to-video"
9075
9319
  },
9076
9320
  {
9077
9321
  nodeType: "video",
@@ -9081,85 +9325,79 @@ var AUTO_MODEL_REGISTRY = [
9081
9325
  // backend remaps reference → image_url at request build time
9082
9326
  // (see node-run-execution.ts → "primary image port" remap) since
9083
9327
  // kling-avatar's variant only accepts image_url.
9084
- standard: { model: "kling-avatar", variant: "image-to-video" },
9085
- fast: { model: "kling-avatar", variant: "image-to-video" }
9328
+ model: "kling-avatar",
9329
+ variant: "image-to-video"
9086
9330
  },
9087
9331
  {
9088
9332
  nodeType: "video",
9089
9333
  inputPattern: "frames-with-audio",
9090
9334
  // Kling Avatar does not accept end_image_url, so route frame+audio
9091
9335
  // runs through LTX's audio-capable image-to-video variant.
9092
- standard: { model: "ltx-2.3", variant: "image-to-video" },
9093
- fast: { model: "ltx-2.3", variant: "image-to-video" }
9336
+ model: "ltx-2.3",
9337
+ variant: "image-to-video"
9094
9338
  },
9095
9339
  {
9096
9340
  nodeType: "video",
9097
9341
  inputPattern: "video-to-video",
9098
- standard: { model: "grok-imagine-video", variant: "video-to-video" },
9099
- fast: { model: "grok-imagine-video", variant: "video-to-video" }
9342
+ model: "kling-o3",
9343
+ variant: "video-to-video"
9100
9344
  },
9101
9345
  // ── Image ───────────────────────────────────────────────────────────
9102
9346
  {
9103
9347
  nodeType: "image",
9104
9348
  inputPattern: "text-to-image",
9105
- standard: { model: "nano-banana-pro", variant: "text-to-image" },
9106
- fast: { model: "flux-schnell", variant: "text-to-image" }
9349
+ model: "nano-banana-pro",
9350
+ variant: "text-to-image"
9107
9351
  },
9108
9352
  {
9109
9353
  nodeType: "image",
9110
9354
  inputPattern: "image-to-image",
9111
- standard: { model: "nano-banana-pro", variant: "image-to-image" },
9112
- fast: { model: "grok-imagine", variant: "image-to-image" }
9355
+ model: "nano-banana-pro",
9356
+ variant: "image-to-image"
9113
9357
  },
9114
9358
  // ── Text ────────────────────────────────────────────────────────────
9115
9359
  {
9116
9360
  nodeType: "text",
9117
9361
  inputPattern: "default",
9118
- standard: { model: "gemini-3.1-pro", variant: "text" },
9119
- fast: { model: "gemini-3.1-pro", variant: "text" }
9362
+ model: "gemini-3.1-pro",
9363
+ variant: "text"
9120
9364
  },
9121
9365
  {
9122
9366
  nodeType: "text",
9123
9367
  inputPattern: "with-video",
9124
- standard: { model: "gemini-3.1-pro", variant: "text" },
9125
- fast: { model: "gemini-3.1-pro", variant: "text" }
9368
+ model: "gemini-3.1-pro",
9369
+ variant: "text"
9126
9370
  },
9127
9371
  // ── Audio ───────────────────────────────────────────────────────────
9128
9372
  {
9129
9373
  nodeType: "audio",
9130
9374
  inputPattern: "text-to-speech",
9131
- standard: { model: "eleven_v3", variant: "text-to-speech" },
9132
- fast: { model: "eleven_turbo_v2_5", variant: "text-to-speech" }
9375
+ model: "eleven_v3",
9376
+ variant: "text-to-speech"
9133
9377
  },
9134
9378
  {
9135
9379
  nodeType: "audio",
9136
9380
  inputPattern: "audio-to-audio",
9137
- standard: {
9138
- model: "elevenlabs-voice-changer",
9139
- variant: "audio-to-audio"
9140
- },
9141
- fast: {
9142
- model: "elevenlabs-voice-changer",
9143
- variant: "audio-to-audio"
9144
- }
9381
+ model: "elevenlabs-voice-changer",
9382
+ variant: "audio-to-audio"
9145
9383
  },
9146
9384
  {
9147
9385
  nodeType: "audio",
9148
9386
  inputPattern: "text-to-sfx",
9149
- standard: { model: "elevenlabs-sfx", variant: "text-to-sfx" },
9150
- fast: { model: "elevenlabs-sfx", variant: "text-to-sfx" }
9387
+ model: "elevenlabs-sfx",
9388
+ variant: "text-to-sfx"
9151
9389
  },
9152
9390
  {
9153
9391
  nodeType: "audio",
9154
9392
  inputPattern: "text-to-music",
9155
- standard: { model: "elevenlabs-music", variant: "text-to-music" },
9156
- fast: { model: "elevenlabs-music", variant: "text-to-music" }
9393
+ model: "elevenlabs-music",
9394
+ variant: "text-to-music"
9157
9395
  },
9158
9396
  {
9159
9397
  nodeType: "audio",
9160
9398
  inputPattern: "speech-to-text",
9161
- standard: { model: "scribe_v2", variant: "speech-to-text" },
9162
- fast: { model: "scribe_v2", variant: "speech-to-text" }
9399
+ model: "scribe_v2",
9400
+ variant: "speech-to-text"
9163
9401
  }
9164
9402
  ];
9165
9403
  var AutoModelProxy = class {
@@ -9181,17 +9419,13 @@ var AutoModelProxy = class {
9181
9419
  resolve(input) {
9182
9420
  switch (input.nodeType) {
9183
9421
  case "video":
9184
- return this.resolveVideo(
9185
- input.connectedInputs,
9186
- input.tier,
9187
- input.canvasVideoInputRoutingEnabled === true
9188
- );
9422
+ return this.resolveVideo(input.connectedInputs);
9189
9423
  case "image":
9190
- return this.resolveImage(input.connectedInputs, input.tier);
9424
+ return this.resolveImage(input.connectedInputs);
9191
9425
  case "text":
9192
- return this.resolveText(input.connectedInputs, input.tier);
9426
+ return this.resolveText(input.connectedInputs);
9193
9427
  case "audio":
9194
- return this.resolveAudio(input.variant, input.tier);
9428
+ return this.resolveAudio(input.variant);
9195
9429
  default:
9196
9430
  return {
9197
9431
  valid: false,
@@ -9204,13 +9438,22 @@ var AutoModelProxy = class {
9204
9438
  return this.registry;
9205
9439
  }
9206
9440
  // ── Private resolvers ───────────────────────────────────────────────
9207
- resolveVideo(ci, tier, canvasVideoInputRoutingEnabled) {
9441
+ resolveVideo(ci) {
9208
9442
  const hasAnyImage = ci.hasImage || ci.hasStartingImage || ci.hasEndImage || ci.hasReferenceImage;
9209
9443
  if (ci.hasVideo && hasAnyImage) {
9210
- return {
9211
- valid: false,
9212
- errorMessage: "You cannot have an image and a video as input at the same time"
9213
- };
9444
+ if (ci.hasEndImage) {
9445
+ return {
9446
+ valid: false,
9447
+ errorMessage: "A last frame cannot be used with a video input"
9448
+ };
9449
+ }
9450
+ if (ci.hasAudio) {
9451
+ return {
9452
+ valid: false,
9453
+ errorMessage: "Audio input is not supported with image and video inputs together"
9454
+ };
9455
+ }
9456
+ return this.lookupRoute("video", "video-with-image-references");
9214
9457
  }
9215
9458
  if (ci.hasEndImage && !ci.hasStartingImage && !ci.hasReferenceImage && !ci.hasImage && !ci.hasVideo) {
9216
9459
  return {
@@ -9225,17 +9468,13 @@ var AutoModelProxy = class {
9225
9468
  };
9226
9469
  }
9227
9470
  if (ci.hasAudio && ci.hasVideo) {
9228
- return this.lookupRoute("video", "video-with-audio", tier);
9471
+ return this.lookupRoute("video", "video-with-audio");
9229
9472
  }
9230
9473
  if (ci.hasAudio && (ci.hasStartingImage || ci.hasImage) && ci.hasEndImage) {
9231
- return canvasVideoInputRoutingEnabled ? this.lookupRoute("video", "frames-with-audio", tier) : {
9232
- valid: true,
9233
- model: "ltx-2.3-audio",
9234
- variant: "image-to-video"
9235
- };
9474
+ return this.lookupRoute("video", "frames-with-audio");
9236
9475
  }
9237
9476
  if (ci.hasAudio && hasAnyImage) {
9238
- return this.lookupRoute("video", "image-with-audio", tier);
9477
+ return this.lookupRoute("video", "image-with-audio");
9239
9478
  }
9240
9479
  if (ci.hasAudio) {
9241
9480
  return {
@@ -9244,48 +9483,40 @@ var AutoModelProxy = class {
9244
9483
  };
9245
9484
  }
9246
9485
  if (ci.hasVideo) {
9247
- return this.lookupRoute("video", "video-to-video", tier);
9486
+ return this.lookupRoute("video", "video-to-video");
9248
9487
  }
9249
9488
  const hasFrameInput = ci.hasStartingImage || ci.hasEndImage || ci.hasImage;
9250
9489
  if (ci.hasReferenceImage && hasFrameInput) {
9251
- return this.lookupRoute(
9252
- "video",
9253
- "reference-with-frames-to-video",
9254
- tier
9255
- );
9490
+ return this.lookupRoute("video", "reference-with-frames-to-video");
9256
9491
  }
9257
9492
  if (ci.hasReferenceImage) {
9258
- return this.lookupRoute("video", "reference-to-video", tier);
9493
+ return this.lookupRoute("video", "reference-to-video");
9259
9494
  }
9260
9495
  if (ci.hasStartingImage || ci.hasImage) {
9261
9496
  if (ci.hasEndImage) {
9262
- return this.lookupRoute(
9263
- "video",
9264
- "first-last-frame-to-video",
9265
- tier
9266
- );
9497
+ return this.lookupRoute("video", "first-last-frame-to-video");
9267
9498
  }
9268
- return this.lookupRoute("video", "image-to-video", tier);
9499
+ return this.lookupRoute("video", "image-to-video");
9269
9500
  }
9270
- return this.lookupRoute("video", "text-only", tier);
9501
+ return this.lookupRoute("video", "text-only");
9271
9502
  }
9272
- resolveImage(ci, tier) {
9503
+ resolveImage(ci) {
9273
9504
  if (ci.hasImage || ci.hasReferenceImage) {
9274
- return this.lookupRoute("image", "image-to-image", tier);
9505
+ return this.lookupRoute("image", "image-to-image");
9275
9506
  }
9276
- return this.lookupRoute("image", "text-to-image", tier);
9507
+ return this.lookupRoute("image", "text-to-image");
9277
9508
  }
9278
- resolveAudio(variant, tier) {
9509
+ resolveAudio(variant) {
9279
9510
  const pattern = variant ?? "text-to-speech";
9280
- return this.lookupRoute("audio", pattern, tier);
9511
+ return this.lookupRoute("audio", pattern);
9281
9512
  }
9282
- resolveText(ci, tier) {
9513
+ resolveText(ci) {
9283
9514
  if (ci.hasVideo) {
9284
- return this.lookupRoute("text", "with-video", tier);
9515
+ return this.lookupRoute("text", "with-video");
9285
9516
  }
9286
- return this.lookupRoute("text", "default", tier);
9517
+ return this.lookupRoute("text", "default");
9287
9518
  }
9288
- lookupRoute(nodeType, inputPattern, tier) {
9519
+ lookupRoute(nodeType, inputPattern) {
9289
9520
  const route = this.lookup.get(`${nodeType}:${inputPattern}`);
9290
9521
  if (!route) {
9291
9522
  return {
@@ -9293,8 +9524,7 @@ var AutoModelProxy = class {
9293
9524
  errorMessage: `No auto model route for ${nodeType}:${inputPattern}`
9294
9525
  };
9295
9526
  }
9296
- const entry = tier === "fast" ? route.fast : route.standard;
9297
- return { valid: true, model: entry.model, variant: entry.variant };
9527
+ return { valid: true, model: route.model, variant: route.variant };
9298
9528
  }
9299
9529
  };
9300
9530
 
@@ -9329,6 +9559,19 @@ var AUDIO_NODE_WIDTH = 380;
9329
9559
  var AUDIO_NODE_ESTIMATED_HEIGHT = 420;
9330
9560
  var PDF_NODE_DEFAULT_WIDTH = 440;
9331
9561
  var NODE_CARD_OVERHEAD = 252;
9562
+ var ASPECT_RATIOS = [
9563
+ "21:9",
9564
+ "16:9",
9565
+ "3:2",
9566
+ "4:3",
9567
+ "5:4",
9568
+ "1:1",
9569
+ "4:5",
9570
+ "3:4",
9571
+ "2:3",
9572
+ "9:16",
9573
+ "9:21"
9574
+ ];
9332
9575
  var MODALITIES = {
9333
9576
  text: { modelCategory: "text" },
9334
9577
  image: { modelCategory: "image" },
@@ -10072,7 +10315,8 @@ var TemplatePortSummarySchema = external_exports.object({
10072
10315
  label: external_exports.string(),
10073
10316
  handleType: external_exports.string(),
10074
10317
  required: external_exports.boolean().optional(),
10075
- maxConnections: external_exports.number().int().positive().optional()
10318
+ maxConnections: external_exports.number().int().positive().optional(),
10319
+ outputAspectRatio: external_exports.enum(ASPECT_RATIOS).nullable().optional()
10076
10320
  });
10077
10321
 
10078
10322
  // ../api-contract/src/canvas.ts
@@ -10122,6 +10366,8 @@ var ModelConfigSchema = external_exports.object({
10122
10366
  model: external_exports.string(),
10123
10367
  variant: external_exports.string(),
10124
10368
  tier: external_exports.string().nullable().optional(),
10369
+ /** HeyGen Avatar V only: the Runware avatar id for this node. */
10370
+ heygenAvatar: external_exports.string().optional(),
10125
10371
  // Magic-resize stores its per-run config here so each version carries the
10126
10372
  // ratios + resolution that produced it.
10127
10373
  ratios: external_exports.array(external_exports.string()).optional(),
@@ -10146,25 +10392,15 @@ var ModelConfigInputSchema = external_exports.object({
10146
10392
  model: external_exports.string(),
10147
10393
  variant: external_exports.string().refine(isValidGenerationMode, "Invalid generation mode"),
10148
10394
  tier: external_exports.string().nullable().optional(),
10395
+ /** HeyGen Avatar V only: the Runware avatar id for this node. */
10396
+ heygenAvatar: external_exports.string().trim().min(1).optional(),
10149
10397
  /** better-font-32b only: the font the model renders the requested text in (supplied to the model as a rendered reference image). */
10150
10398
  font: external_exports.object({
10151
10399
  source: external_exports.enum(["custom", "google"]),
10152
10400
  familyName: external_exports.string()
10153
10401
  }).optional()
10154
10402
  });
10155
- var OutputAspectRatioSchema = external_exports.enum([
10156
- "21:9",
10157
- "16:9",
10158
- "4:3",
10159
- "5:4",
10160
- "3:2",
10161
- "1:1",
10162
- "2:3",
10163
- "3:4",
10164
- "4:5",
10165
- "9:16",
10166
- "9:21"
10167
- ]);
10403
+ var OutputAspectRatioSchema = external_exports.enum(ASPECT_RATIOS);
10168
10404
  var OutputResolutionPresetSchema = external_exports.enum([
10169
10405
  "360p",
10170
10406
  "480p",
@@ -10288,6 +10524,16 @@ var NodeVersionResponseSchema = external_exports.object({
10288
10524
  */
10289
10525
  modelDisplayName: external_exports.string().nullable().optional(),
10290
10526
  asset: AssetResponseSchema.nullable(),
10527
+ /**
10528
+ * For a version produced by a node tool (background removal, subtitles,
10529
+ * GIF, upscale — i.e. `sourceToolName` is set), the source asset the tool
10530
+ * transformed. A tool runs in place on a file node, so the input lives on
10531
+ * an earlier version that the bulk content response does not ship; this
10532
+ * surfaces it so consumers (e.g. Playground history) can show what the tool
10533
+ * ran on without re-fetching the node's full version list. Populated only
10534
+ * when the caller opts in via `includeToolInputReferences`.
10535
+ */
10536
+ toolInputAsset: AssetResponseSchema.nullable().optional(),
10291
10537
  pdfExtraction: PdfExtractionSchema.nullable().optional(),
10292
10538
  /** Present when an active node run exists for this version (e.g. PDF extraction in progress). */
10293
10539
  nodeRunId: external_exports.string().uuid().nullable().optional(),
@@ -10310,7 +10556,12 @@ var MagicResizeOutputSchema = external_exports.object({
10310
10556
  assetId: external_exports.string().nullable(),
10311
10557
  assetUrl: external_exports.string().nullable(),
10312
10558
  previewUrl: external_exports.string().nullable(),
10313
- failed: external_exports.boolean().optional()
10559
+ failed: external_exports.boolean().optional(),
10560
+ // User-facing failure reason for a `failed` layer, mirroring the error
10561
+ // message + severity a regular media node shows (e.g. a provider error vs.
10562
+ // a content-moderation warning). Absent on successful layers.
10563
+ errorMessage: external_exports.string().nullish(),
10564
+ errorSeverity: external_exports.enum(["error", "warning"]).nullish()
10314
10565
  });
10315
10566
  var NodeResponseSchema = external_exports.object({
10316
10567
  id: external_exports.string().uuid(),
@@ -10372,6 +10623,8 @@ var NodeResponseSchema = external_exports.object({
10372
10623
  childNodeIds: external_exports.array(external_exports.string().uuid()).optional(),
10373
10624
  /** Per-ratio outputs for magic-resize nodes (from CRDT); one per variant. */
10374
10625
  magicResizeOutputs: external_exports.array(MagicResizeOutputSchema).nullable().optional(),
10626
+ /** User id of the team member who created the node, or null when unknown. */
10627
+ createdByUserId: external_exports.string().uuid().nullable().optional(),
10375
10628
  versions: external_exports.array(NodeVersionResponseSchema),
10376
10629
  createdAt: external_exports.string().datetime(),
10377
10630
  updatedAt: external_exports.string().datetime()
@@ -10392,7 +10645,7 @@ var CanvasMediaPresetSchema = external_exports.object({
10392
10645
  aspectRatio: OutputAspectRatioSchema.optional(),
10393
10646
  resolution: OutputResolutionSchema.optional(),
10394
10647
  fast: external_exports.boolean().optional(),
10395
- numVariations: external_exports.number().int().min(1).max(10).optional()
10648
+ numVariations: external_exports.number().int().min(1).max(MAX_VARIATIONS).optional()
10396
10649
  }).optional(),
10397
10650
  video: external_exports.object({
10398
10651
  model: external_exports.string().optional(),
@@ -10401,13 +10654,13 @@ var CanvasMediaPresetSchema = external_exports.object({
10401
10654
  resolution: OutputResolutionSchema.optional(),
10402
10655
  duration: external_exports.number().int().positive().optional(),
10403
10656
  fast: external_exports.boolean().optional(),
10404
- numVariations: external_exports.number().int().min(1).max(10).optional()
10657
+ numVariations: external_exports.number().int().min(1).max(MAX_VARIATIONS).optional()
10405
10658
  }).optional(),
10406
10659
  text: external_exports.object({
10407
10660
  model: external_exports.string().optional(),
10408
10661
  mode: external_exports.string().optional(),
10409
10662
  fast: external_exports.boolean().optional(),
10410
- numVariations: external_exports.number().int().min(1).max(10).optional()
10663
+ numVariations: external_exports.number().int().min(1).max(MAX_VARIATIONS).optional()
10411
10664
  }).optional(),
10412
10665
  audio: external_exports.object({
10413
10666
  model: external_exports.string().optional(),
@@ -10449,7 +10702,24 @@ var CanvasContentQuerySchema = external_exports.object({
10449
10702
  * so the model reads the summary instead of a URL. The web app does
10450
10703
  * not set this, so it always receives URLs.
10451
10704
  */
10452
- mediaSummaries: QueryBooleanSchema.optional()
10705
+ mediaSummaries: QueryBooleanSchema.optional(),
10706
+ /**
10707
+ * Opt-in: resolve the source asset each node-tool version transformed
10708
+ * onto `version.toolInputAsset` (see that field). Off by default so the
10709
+ * common canvas/agent/MCP reads never pay the extra version lookup; the
10710
+ * Playground history loader sets it to restore tool-run input previews.
10711
+ */
10712
+ includeToolInputReferences: QueryBooleanSchema.optional(),
10713
+ /**
10714
+ * Opt-in: ship EVERY asset-bearing version for generation nodes
10715
+ * (image/video/audio) with more than one output, so a
10716
+ * native-variations node (one node, N output versions) can rebuild
10717
+ * its full output set from a bulk read. Resolved in a bounded batched
10718
+ * pass like `includeToolInputReferences`; off by default so common
10719
+ * reads pay nothing. Unlike `includeAllVersions` it needs no nodeId
10720
+ * scoping.
10721
+ */
10722
+ includeVariationVersions: QueryBooleanSchema.optional()
10453
10723
  }).refine(
10454
10724
  (query) => query.includeAllVersions !== true || query.nodeId !== void 0,
10455
10725
  {
@@ -10484,7 +10754,7 @@ var RunNodeBodySchema = external_exports.object({
10484
10754
  outputDuration: external_exports.number().int().positive().optional(),
10485
10755
  outputResolution: OutputResolutionSchema.optional(),
10486
10756
  seed: external_exports.number().int().nonnegative().max(MAX_SEED).optional(),
10487
- numVariations: external_exports.number().int().min(1).max(10).optional(),
10757
+ numVariations: external_exports.number().int().min(1).max(MAX_VARIATIONS).optional(),
10488
10758
  /**
10489
10759
  * Dev-only: force a specific generation provider for the run. When set,
10490
10760
  * `prepareGenerationRequest` restricts variant selection to the named
@@ -10558,7 +10828,7 @@ var RunImageToolBodySchema = external_exports.object({
10558
10828
  canvasId: external_exports.string().uuid().optional(),
10559
10829
  toolName: external_exports.string().trim().min(1),
10560
10830
  toolConfig: external_exports.record(external_exports.unknown()).optional(),
10561
- numVariations: external_exports.number().int().min(1).max(10).optional(),
10831
+ numVariations: external_exports.number().int().min(1).max(MAX_VARIATIONS).optional(),
10562
10832
  /** Base64-encoded PNG mask image for inpainting (white = areas to regenerate). */
10563
10833
  maskImageBase64: external_exports.string().optional()
10564
10834
  });
@@ -11232,7 +11502,12 @@ var canvasContract = c7.router({
11232
11502
  failedLayers: external_exports.array(
11233
11503
  external_exports.object({
11234
11504
  layerIndex: external_exports.number().int(),
11235
- ratio: external_exports.string()
11505
+ ratio: external_exports.string(),
11506
+ // User-facing failure reason + severity (mirrors a
11507
+ // regular media node's error/warning). Optional so
11508
+ // a new client tolerates an older backend.
11509
+ errorMessage: external_exports.string().nullish(),
11510
+ errorSeverity: external_exports.enum(["error", "warning"]).nullish()
11236
11511
  })
11237
11512
  ).nullish()
11238
11513
  }),
@@ -11561,7 +11836,7 @@ var canvasContract = c7.router({
11561
11836
  * every intermediate node — see `BulkNodeRunService`. Used by the
11562
11837
  * template box's variation stepper. Defaults to 1 when omitted.
11563
11838
  */
11564
- numVariations: external_exports.number().int().min(1).max(10).optional(),
11839
+ numVariations: external_exports.number().int().min(1).max(MAX_VARIATIONS).optional(),
11565
11840
  /**
11566
11841
  * Node ids whose audio-to-video duration should be matched to
11567
11842
  * connected generated audio after upstream audio nodes finish.
@@ -12073,6 +12348,8 @@ var GenerationRunBodySchema = external_exports.object({
12073
12348
  model: external_exports.string().trim().min(1),
12074
12349
  tier: external_exports.string().optional(),
12075
12350
  prompt: external_exports.string().trim().min(1).optional(),
12351
+ /** Selected avatar id from Runware's HeyGen Avatar V catalog. */
12352
+ heygenAvatar: external_exports.string().trim().min(1).optional(),
12076
12353
  imageUrls: external_exports.array(external_exports.string().url()).optional(),
12077
12354
  endImageUrls: external_exports.array(external_exports.string().url()).optional(),
12078
12355
  referenceImageUrls: external_exports.array(external_exports.string().url()).optional(),
@@ -12090,7 +12367,7 @@ var GenerationRunBodySchema = external_exports.object({
12090
12367
  duration: external_exports.number().int().positive().optional(),
12091
12368
  resolution: OutputResolutionSchema.optional(),
12092
12369
  seed: external_exports.number().int().nonnegative().max(MAX_SEED).optional(),
12093
- numVariations: external_exports.number().int().min(1).max(10).optional(),
12370
+ numVariations: external_exports.number().int().min(1).max(MAX_VARIATIONS).optional(),
12094
12371
  /**
12095
12372
  * Whether the variant should generate audio. Only honored on variants
12096
12373
  * whose registry entry declares `falAudioField`. Defaults to `true`.
@@ -12357,6 +12634,10 @@ var GenerationHistoryItemSchema = external_exports.object({
12357
12634
  id: external_exports.string(),
12358
12635
  type: external_exports.enum(["media", "text"]),
12359
12636
  model: external_exports.string(),
12637
+ // Speed tier (e.g. "fast") the job ran at, when it ran at a non-standard
12638
+ // one. Optional so older clients ignore it and the field is absent for
12639
+ // standard-tier and text generations.
12640
+ tier: external_exports.string().nullable().optional(),
12360
12641
  status: GenerationJobStatusSchema,
12361
12642
  creditCost: external_exports.number().nullable(),
12362
12643
  createdAt: external_exports.string(),
@@ -12415,6 +12696,22 @@ var generationContract = c9.router({
12415
12696
  200: ListGenerativeModelsResponseSchema
12416
12697
  }
12417
12698
  },
12699
+ listPublicGenerativeModels: {
12700
+ method: "GET",
12701
+ path: "/generation/models/public",
12702
+ query: external_exports.object({ category: ModelCategorySchema }),
12703
+ responses: {
12704
+ 200: ListGenerativeModelsResponseSchema
12705
+ },
12706
+ summary: "List generally-available models without authentication (logged-out home)"
12707
+ },
12708
+ listHeygenAvatarVAvatars: {
12709
+ method: "GET",
12710
+ path: "/generation/heygen-avatar-v-avatars",
12711
+ responses: {
12712
+ 200: external_exports.object({ avatars: external_exports.array(external_exports.string()) })
12713
+ }
12714
+ },
12418
12715
  getAutoModelDefaults: {
12419
12716
  method: "GET",
12420
12717
  path: "/generation/auto-model-defaults",
@@ -12422,6 +12719,14 @@ var generationContract = c9.router({
12422
12719
  200: AutoModelDefaultsResponseSchema
12423
12720
  }
12424
12721
  },
12722
+ getPublicAutoModelDefaults: {
12723
+ method: "GET",
12724
+ path: "/generation/auto-model-defaults/public",
12725
+ responses: {
12726
+ 200: AutoModelDefaultsResponseSchema
12727
+ },
12728
+ summary: "Auto-model defaults without authentication (logged-out home)"
12729
+ },
12425
12730
  listGenerationHistory: {
12426
12731
  method: "GET",
12427
12732
  path: "/generation/history",
@@ -12700,6 +13005,17 @@ var profileContract = c11.router({
12700
13005
  // ../api-contract/src/share.ts
12701
13006
  var c12 = initContract();
12702
13007
  var ShareModeSchema = external_exports.enum(["restricted", "view"]);
13008
+ var SharedAssetResponseSchema = external_exports.object({
13009
+ id: external_exports.string().uuid(),
13010
+ filename: external_exports.string(),
13011
+ fileType: FileTypeSchema,
13012
+ contentType: external_exports.string().nullable(),
13013
+ // Presigned media URL; null only when the underlying storage is missing.
13014
+ mediaUrl: external_exports.string().nullable(),
13015
+ width: external_exports.number().int().nullable(),
13016
+ height: external_exports.number().int().nullable(),
13017
+ durationInMs: external_exports.number().int().nullable()
13018
+ });
12703
13019
  var ShareCanvasResponseSchema = external_exports.object({
12704
13020
  id: external_exports.string().uuid(),
12705
13021
  projectId: external_exports.string().uuid(),
@@ -12759,6 +13075,16 @@ var shareContract = c12.router({
12759
13075
  }
12760
13076
  },
12761
13077
  // Public endpoint — no auth required
13078
+ getSharedAsset: {
13079
+ method: "GET",
13080
+ path: "/share/assets/:assetId",
13081
+ pathParams: external_exports.object({ assetId: external_exports.string().uuid() }),
13082
+ responses: {
13083
+ 200: SharedAssetResponseSchema,
13084
+ 404: MessageResponseSchema
13085
+ }
13086
+ },
13087
+ // Public endpoint — no auth required
12762
13088
  getSharedCanvas: {
12763
13089
  method: "GET",
12764
13090
  path: "/share/projects/:projectId/canvases/:canvasId",
@@ -13894,7 +14220,7 @@ function isMajorUpgrade(current, latest) {
13894
14220
  return l[0] > c17[0];
13895
14221
  }
13896
14222
  function shouldShowUpgradeNotice(ctx = {}) {
13897
- const current = ctx.current ?? "0.7.0";
14223
+ const current = ctx.current ?? "0.9.0";
13898
14224
  const latest = ctx.latest ?? serverLatestVersion;
13899
14225
  const env = ctx.env ?? process.env;
13900
14226
  const isTty = ctx.isTty ?? Boolean(process.stderr.isTTY);
@@ -13915,7 +14241,7 @@ Upgrade: npm install -g @melius-ai/cli@latest (or: brew update && brew upgrade
13915
14241
  function printUpgradeNoticeIfNeeded(ctx = {}) {
13916
14242
  try {
13917
14243
  if (!shouldShowUpgradeNotice(ctx)) return;
13918
- const current = ctx.current ?? "0.7.0";
14244
+ const current = ctx.current ?? "0.9.0";
13919
14245
  const latest = ctx.latest ?? serverLatestVersion;
13920
14246
  process.stderr.write(formatUpgradeNotice(current, latest));
13921
14247
  } catch {
@@ -18599,10 +18925,10 @@ function registerAssetCommands(program2, getOutputOpts) {
18599
18925
  "Search and place the team's Files (DAM) library assets. Requires a Pro or Enterprise plan"
18600
18926
  );
18601
18927
  asset.command("search").description(
18602
- "Search the team's Files/DAM library (uploads + past generations) by meaning (semantic), with exact filename/title matches ranked first. Returns up to 20 relevant matches per page. Scope to a folder with --folder-id; omit the query and pass --folder-id to bring in the WHOLE folder"
18928
+ "Search the team's Files/DAM library (uploads + past generations) by meaning (semantic), with exact filename/title matches ranked first. Returns up to 20 relevant matches per page. Filter by asset source with --source, scope to a folder with --folder-id, or filter by tag with --tags; omit the query and pass --folder-id or --tags to bring in the whole folder / all tagged assets"
18603
18929
  ).argument(
18604
18930
  "[query]",
18605
- 'Search terms \u2014 a filename or a description of the content, e.g. "Product_Shot_05" or "black jacket shot". Optional: OMIT it and pass --folder-id to list EVERY asset in that folder'
18931
+ 'Search terms \u2014 an exact filename (including its extension) or a description of the content, e.g. "Product_Shot_05.png" or "black jacket shot". Optional: OMIT it and pass --folder-id to list EVERY asset in that folder'
18606
18932
  ).option("--limit <n>", "Max results per page (1-20, default 20)", "20").option(
18607
18933
  "--offset <n>",
18608
18934
  "Skip the first N matches to page deeper (default 0). Page with --limit until the running count reaches the response's total",
@@ -18610,12 +18936,18 @@ function registerAssetCommands(program2, getOutputOpts) {
18610
18936
  ).option(
18611
18937
  "--file-type <type>",
18612
18938
  "Filter by media type: image, video, audio, pdf, or text"
18939
+ ).option(
18940
+ "--source <source>",
18941
+ "Filter by asset source: upload (member-uploaded files) or generated (media created in Melius)"
18613
18942
  ).option(
18614
18943
  "--canvas-id <canvasId>",
18615
18944
  "Scope to one canvas's assets (a filename match within that canvas). Omit to search the whole team library by meaning, exact matches first (the default)"
18616
18945
  ).option(
18617
18946
  "--folder-id <folderId>",
18618
18947
  "Scope to one Files folder AND its subfolders. WITH a query, filters within the folder; WITHOUT a query, returns every asset in the folder. Get folder ids from mel asset folders"
18948
+ ).option(
18949
+ "--tags <names>",
18950
+ "Filter to assets carrying ALL of these tags (AND), comma-separated names (case-insensitive), e.g. --tags brand or --tags brand,hero. An unknown or misspelled name matches nothing. Combine with a query, or use alone (or with --folder-id) to bring in every asset carrying them"
18619
18951
  ).addHelpText(
18620
18952
  "after",
18621
18953
  `
@@ -18626,19 +18958,23 @@ Resolve a folder name to an id with mel asset folders first.
18626
18958
 
18627
18959
  Examples:
18628
18960
  $ mel asset search "black jacket shot"
18961
+ $ mel asset search "Product_Shot_05.PNG" --source upload
18962
+ $ mel asset search "shark close-up" --source generated
18629
18963
  $ mel asset search "logo" --file-type image --limit 10
18630
18964
  $ mel asset search "logo" --limit 20 --offset 20
18631
18965
  $ mel asset search "shark" --folder-id <folderId> # find within a folder
18632
- $ mel asset search --folder-id <folderId> # bring in the WHOLE folder`
18966
+ $ mel asset search --folder-id <folderId> # bring in the WHOLE folder
18967
+ $ mel asset search "logo" --tags brand # find tagged assets
18968
+ $ mel asset search --tags brand,hero # every asset with BOTH tags`
18633
18969
  ).action(
18634
18970
  async (query, opts) => {
18635
- if (query === void 0 && opts.folderId === void 0) {
18971
+ if (query === void 0 && opts.folderId === void 0 && opts.tags === void 0) {
18636
18972
  writeError(
18637
18973
  formatError(
18638
18974
  "USAGE_ERROR",
18639
- "Provide a search query, or --folder-id to bring in a whole folder",
18975
+ "Provide a search query, --folder-id to bring in a whole folder, or --tags to bring in tagged assets",
18640
18976
  {
18641
- suggestion: "Run mel asset folders to list folder ids, then: mel asset search --folder-id <folderId>"
18977
+ suggestion: "Run mel asset folders to list folder ids, then: mel asset search --folder-id <folderId> (or: mel asset search --tags <name>)"
18642
18978
  }
18643
18979
  ),
18644
18980
  EXIT_USAGE_ERROR
@@ -18653,12 +18989,16 @@ Examples:
18653
18989
  ...opts.fileType !== void 0 && {
18654
18990
  fileType: opts.fileType
18655
18991
  },
18992
+ ...opts.source !== void 0 && {
18993
+ source: opts.source
18994
+ },
18656
18995
  ...opts.canvasId !== void 0 && {
18657
18996
  canvasId: opts.canvasId
18658
18997
  },
18659
18998
  ...opts.folderId !== void 0 && {
18660
18999
  folderId: opts.folderId
18661
- }
19000
+ },
19001
+ ...opts.tags !== void 0 && { tags: opts.tags }
18662
19002
  }
18663
19003
  });
18664
19004
  if (result.status !== 200) {
@@ -18865,7 +19205,7 @@ function withDefaultHelp(args) {
18865
19205
  }
18866
19206
  function createProgram() {
18867
19207
  const program2 = new Command();
18868
- program2.name("mel").description("Mel \u2014 Melius agent CLI").version("0.7.0").showSuggestionAfterError(true).option("--json", "Force JSON output (default)").option("--text", "Human-readable output").option(
19208
+ program2.name("mel").description("Mel \u2014 Melius agent CLI").version("0.9.0").showSuggestionAfterError(true).option("--json", "Force JSON output (default)").option("--text", "Human-readable output").option(
18869
19209
  "--fields <fields>",
18870
19210
  "Select specific output fields (comma-separated)"
18871
19211
  ).option("--quiet", "Suppress output, exit code only").option("--verbose", "Log request/response metadata to stderr").addHelpText(
package/dist/src/index.js CHANGED
@@ -2,7 +2,7 @@ import { createRequire } from 'node:module'; const require = createRequire(impor
2
2
  import {
3
3
  createProgram,
4
4
  withDefaultHelp
5
- } from "../chunk-5QT6WMID.js";
5
+ } from "../chunk-24FW76XZ.js";
6
6
  export {
7
7
  createProgram,
8
8
  withDefaultHelp
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@melius-ai/cli",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "The Melius command-line interface — create canvases and run AI image/video generations from your terminal",
5
5
  "keywords": [
6
6
  "ai",