@melius-ai/cli 0.6.0 → 0.8.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
@@ -261,17 +261,31 @@ Requires the Pro or Enterprise plan.
261
261
 
262
262
  | Command | Description |
263
263
  | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
264
- | `mel asset search <query>` | Search the team's Files/DAM library (uploads + past generations) by meaning; exact matches rank first. Returns up to 20 matches per page |
264
+ | `mel asset search [query]` | Search the team's Files/DAM library (uploads + past generations) by meaning; exact matches rank first. Returns up to 20 matches per page |
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` |
265
266
  | `mel asset place <canvasId> <assetIds...>` | Place existing library assets onto a canvas by id — references the **original** asset (no duplicate) |
266
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
269
 
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
+
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
+
269
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.
270
275
 
271
276
  ```bash
272
277
  # Find an asset, then bring it onto a canvas and wire it
273
278
  mel asset search "black jacket shot" --file-type image
274
279
  mel asset place <canvasId> <assetId1> <assetId2>
280
+
281
+ # Bring in a whole folder: resolve the folder, then search it with no query
282
+ mel asset folders # → [{ id, name, path, fileCount }]
283
+ mel asset search --folder-id <folderId> # every asset in the folder
284
+ mel asset search "shark" --folder-id <folderId> # or filter within it
285
+
286
+ # Filter by tag (AND across tags)
287
+ mel asset search "logo" --tags brand # tagged 'brand'
288
+ mel asset search --tags brand,hero # every asset with BOTH tags
275
289
  ```
276
290
 
277
291
  ### 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-EN744KKV.js";
10
+ } from "../chunk-3PBGQVUH.js";
11
11
 
12
12
  // bin/mel.ts
13
13
  var program = createProgram();
@@ -7911,6 +7911,24 @@ var FileTypeSchema = external_exports.enum([
7911
7911
  "pdf",
7912
7912
  "text"
7913
7913
  ]);
7914
+ var AssetShareModeSchema = external_exports.enum(["team", "anyone"]);
7915
+ var TAG_COLOR_PALETTE = [
7916
+ "hsl(189deg, 72%, 46%)",
7917
+ "hsl(280deg, 60%, 50%)",
7918
+ "hsl(24deg, 76%, 46%)",
7919
+ "hsl(142deg, 58%, 40%)",
7920
+ "hsl(330deg, 64%, 46%)",
7921
+ "hsl(45deg, 68%, 42%)",
7922
+ "hsl(210deg, 66%, 44%)",
7923
+ "hsl(168deg, 55%, 38%)"
7924
+ ];
7925
+ var TagColorSchema = external_exports.enum(TAG_COLOR_PALETTE);
7926
+ var MAX_ASSET_TAG_NAME_LENGTH = 100;
7927
+ var FileTagSchema = external_exports.object({
7928
+ id: external_exports.string().uuid(),
7929
+ name: external_exports.string(),
7930
+ color: external_exports.string()
7931
+ });
7914
7932
  var FileCanvasBacklinkSchema = external_exports.object({
7915
7933
  canvasId: external_exports.string().uuid(),
7916
7934
  projectId: external_exports.string().uuid(),
@@ -7939,6 +7957,9 @@ var FileCardSchema = external_exports.object({
7939
7957
  * The "Open canvas" CTA opens the first directly, or — when there is more
7940
7958
  * than one — prompts the user to pick which to open. */
7941
7959
  canvases: external_exports.array(FileCanvasBacklinkSchema),
7960
+ // The tags assigned to this file (unordered). Always present — empty when
7961
+ // untagged — so the grid can render chips without a per-card fetch.
7962
+ tags: external_exports.array(FileTagSchema),
7942
7963
  // The canvas node whose currently-active version produced this generated
7943
7964
  // asset. The Files grid shows only active-version outputs, so each
7944
7965
  // generated card maps to exactly one such node; this lets the viewer load
@@ -8049,6 +8070,13 @@ var ListFilesQuerySchema = external_exports.object({
8049
8070
  createdByUserId: external_exports.string().uuid().optional(),
8050
8071
  sort: FileSortSchema.default("created_desc"),
8051
8072
  folderId: external_exports.union([external_exports.string().uuid(), external_exports.literal("root")]).optional(),
8073
+ // Filter to files carrying ALL of these tag ids (AND — narrows the set).
8074
+ // Comma-separated in the query string (e.g. `?tagIds=<id1>,<id2>`) because
8075
+ // this GET has no array-query support; absent means no tag filter. Each id
8076
+ // is validated as a uuid after splitting.
8077
+ tagIds: external_exports.string().optional().transform(
8078
+ (value) => value ? value.split(",").map((id) => id.trim()).filter(Boolean) : void 0
8079
+ ).pipe(external_exports.array(external_exports.string().uuid()).optional()),
8052
8080
  // The "Hidden" view: lists files hidden from Files (across all folders)
8053
8081
  // instead of the normal visible listing. Coerced from the query string so
8054
8082
  // `?hidden=true` works; absent / "false" both resolve to `false`.
@@ -8083,15 +8111,34 @@ var ListFilesResponseSchema = external_exports.object({
8083
8111
  cursor: external_exports.string().nullable().optional(),
8084
8112
  hasMore: external_exports.boolean().optional()
8085
8113
  });
8114
+ var FileTabCountsQuerySchema = external_exports.object({
8115
+ q: external_exports.string().optional(),
8116
+ folderId: external_exports.union([external_exports.string().uuid(), external_exports.literal("root")]).optional()
8117
+ });
8118
+ var FileTabCountsResponseSchema = external_exports.object({
8119
+ // "All assets" — always uploads + generations (a file is one or the other).
8120
+ all: external_exports.number().int(),
8121
+ uploads: external_exports.number().int(),
8122
+ generations: external_exports.number().int(),
8123
+ favorites: external_exports.number().int()
8124
+ });
8086
8125
  var AGENT_ASSET_SEARCH_PAGE_SIZE = 20;
8087
8126
  var AgentAssetSearchQuerySchema = external_exports.object({
8088
- query: external_exports.string().trim().min(1).max(500).describe("Asset search query"),
8127
+ query: external_exports.string().trim().min(1).max(500).optional().describe(
8128
+ '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`.'
8129
+ ),
8089
8130
  limit: external_exports.coerce.number().int().min(1).max(AGENT_ASSET_SEARCH_PAGE_SIZE).default(AGENT_ASSET_SEARCH_PAGE_SIZE),
8090
8131
  source: FileSourceSchema.optional(),
8091
8132
  fileType: FileTypeSchema.optional(),
8092
8133
  canvasId: external_exports.string().uuid().optional().describe(
8093
8134
  '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.'
8094
8135
  ),
8136
+ folderId: external_exports.string().uuid().optional().describe(
8137
+ '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.'
8138
+ ),
8139
+ tags: external_exports.string().trim().optional().describe(
8140
+ '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.'
8141
+ ),
8095
8142
  showInThread: external_exports.boolean().optional().describe(
8096
8143
  '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.'
8097
8144
  )
@@ -8112,7 +8159,30 @@ var AgentAssetSearchRequestSchema = AgentAssetSearchQuerySchema.extend(
8112
8159
  var AgentAssetSearchResponseSchema = external_exports.object({
8113
8160
  query: external_exports.string(),
8114
8161
  results: external_exports.array(FileCardSchema),
8115
- total: external_exports.number().int().nonnegative()
8162
+ total: external_exports.number().int().nonnegative(),
8163
+ // Echo the search scope so the picker's "More results" button can re-page
8164
+ // with the SAME scope. Without this, page 2 drops the folder/canvas filter
8165
+ // and pages the whole library (and a query-less folder browse can't page at
8166
+ // all). Absent when the corresponding filter wasn't set.
8167
+ folderId: external_exports.string().uuid().optional(),
8168
+ canvasId: external_exports.string().uuid().optional(),
8169
+ source: FileSourceSchema.optional(),
8170
+ fileType: FileTypeSchema.optional(),
8171
+ // Echoed comma-separated tag filter, so the picker's "More results" re-pages
8172
+ // with the same tags.
8173
+ tags: external_exports.string().optional()
8174
+ });
8175
+ var AgentListAssetFoldersQuerySchema = external_exports.object({});
8176
+ var AgentAssetFolderSchema = external_exports.object({
8177
+ id: external_exports.string().uuid(),
8178
+ name: external_exports.string(),
8179
+ // Full path from the team root (e.g. "Brand / Logos") so the agent can
8180
+ // disambiguate when several folders share a name.
8181
+ path: external_exports.string(),
8182
+ fileCount: external_exports.number().int().nonnegative()
8183
+ });
8184
+ var AgentListAssetFoldersResponseSchema = external_exports.object({
8185
+ folders: external_exports.array(AgentAssetFolderSchema)
8116
8186
  });
8117
8187
  var InitFilesUploadBodySchema = external_exports.object({
8118
8188
  filename: external_exports.string(),
@@ -8144,6 +8214,46 @@ var EmbeddingStorageUsageResponseSchema = external_exports.object({
8144
8214
  usedBytes: external_exports.number(),
8145
8215
  limitBytes: external_exports.number()
8146
8216
  });
8217
+ var AssetTagResponseSchema = external_exports.object({
8218
+ id: external_exports.string().uuid(),
8219
+ teamId: external_exports.string().uuid(),
8220
+ name: external_exports.string(),
8221
+ color: external_exports.string(),
8222
+ createdByUserId: external_exports.string().uuid().nullable(),
8223
+ createdByName: external_exports.string().nullable(),
8224
+ createdAt: external_exports.string().datetime(),
8225
+ updatedAt: external_exports.string().datetime(),
8226
+ // How many of the team's assets carry this tag. Present in listAssetTags;
8227
+ // omitted on single-tag create/update responses (which don't recount).
8228
+ assetCount: external_exports.number().int().nonnegative().optional()
8229
+ });
8230
+ var AssetTagListResponseSchema = external_exports.object({
8231
+ data: external_exports.array(AssetTagResponseSchema)
8232
+ });
8233
+ var AssetTagIdParamsSchema = external_exports.object({
8234
+ tagId: external_exports.string().uuid()
8235
+ });
8236
+ var FileAssetTagParamsSchema = external_exports.object({
8237
+ assetId: external_exports.string().uuid(),
8238
+ tagId: external_exports.string().uuid()
8239
+ });
8240
+ var CreateAssetTagBodySchema = external_exports.object({
8241
+ name: external_exports.string().trim().min(1).max(MAX_ASSET_TAG_NAME_LENGTH),
8242
+ color: TagColorSchema
8243
+ });
8244
+ var UpdateAssetTagBodySchema = external_exports.object({
8245
+ name: external_exports.string().trim().min(1).max(MAX_ASSET_TAG_NAME_LENGTH).optional(),
8246
+ color: TagColorSchema.optional()
8247
+ }).refine((body) => body.name !== void 0 || body.color !== void 0, {
8248
+ message: "Provide a name or color to update"
8249
+ });
8250
+ var AssetTagAssignmentBodySchema = external_exports.object({
8251
+ tagId: external_exports.string().uuid()
8252
+ });
8253
+ var BulkAssetTagBodySchema = external_exports.object({
8254
+ assetIds: BulkFileAssetIdsSchema,
8255
+ tagId: external_exports.string().uuid()
8256
+ });
8147
8257
  var ErrorBodySchema = MessageResponseSchema;
8148
8258
  var filesContract = c2.router({
8149
8259
  createFileFolder: {
@@ -8190,6 +8300,97 @@ var filesContract = c2.router({
8190
8300
  },
8191
8301
  summary: "Delete a Files folder"
8192
8302
  },
8303
+ listAssetTags: {
8304
+ method: "GET",
8305
+ path: "/files/tags",
8306
+ responses: {
8307
+ 200: AssetTagListResponseSchema,
8308
+ 403: ErrorBodySchema
8309
+ },
8310
+ summary: "List team asset tags"
8311
+ },
8312
+ createAssetTag: {
8313
+ method: "POST",
8314
+ path: "/files/tags",
8315
+ body: CreateAssetTagBodySchema,
8316
+ responses: {
8317
+ 201: AssetTagResponseSchema,
8318
+ 403: ErrorBodySchema,
8319
+ 409: ErrorBodySchema
8320
+ },
8321
+ summary: "Create an asset tag"
8322
+ },
8323
+ updateAssetTag: {
8324
+ method: "PATCH",
8325
+ path: "/files/tags/:tagId",
8326
+ pathParams: AssetTagIdParamsSchema,
8327
+ body: UpdateAssetTagBodySchema,
8328
+ responses: {
8329
+ 200: AssetTagResponseSchema,
8330
+ 403: ErrorBodySchema,
8331
+ 404: ErrorBodySchema,
8332
+ 409: ErrorBodySchema
8333
+ },
8334
+ summary: "Rename or recolor an asset tag"
8335
+ },
8336
+ deleteAssetTag: {
8337
+ method: "DELETE",
8338
+ path: "/files/tags/:tagId",
8339
+ pathParams: AssetTagIdParamsSchema,
8340
+ body: null,
8341
+ responses: {
8342
+ 204: c2.noBody(),
8343
+ 403: ErrorBodySchema,
8344
+ 404: ErrorBodySchema
8345
+ },
8346
+ summary: "Delete an asset tag"
8347
+ },
8348
+ addAssetTag: {
8349
+ method: "POST",
8350
+ path: "/files/:assetId/tags",
8351
+ pathParams: FileAssetIdParamsSchema,
8352
+ body: AssetTagAssignmentBodySchema,
8353
+ responses: {
8354
+ 200: FileCardSchema,
8355
+ 403: ErrorBodySchema,
8356
+ 404: ErrorBodySchema
8357
+ },
8358
+ summary: "Add a tag to a file"
8359
+ },
8360
+ removeAssetTag: {
8361
+ method: "DELETE",
8362
+ path: "/files/:assetId/tags/:tagId",
8363
+ pathParams: FileAssetTagParamsSchema,
8364
+ body: null,
8365
+ responses: {
8366
+ 200: FileCardSchema,
8367
+ 403: ErrorBodySchema,
8368
+ 404: ErrorBodySchema
8369
+ },
8370
+ summary: "Remove a tag from a file"
8371
+ },
8372
+ bulkAddAssetTag: {
8373
+ method: "POST",
8374
+ path: "/files/bulk/tag",
8375
+ body: BulkAssetTagBodySchema,
8376
+ responses: {
8377
+ 200: BulkFileActionResponseSchema,
8378
+ 403: ErrorBodySchema,
8379
+ 404: ErrorBodySchema
8380
+ },
8381
+ summary: "Add a tag to files"
8382
+ },
8383
+ bulkRemoveAssetTag: {
8384
+ method: "POST",
8385
+ path: "/files/bulk/untag",
8386
+ body: BulkAssetTagBodySchema,
8387
+ responses: {
8388
+ 200: BulkFileActionResponseSchema,
8389
+ 403: ErrorBodySchema,
8390
+ 404: ErrorBodySchema
8391
+ },
8392
+ summary: "Remove a tag from files"
8393
+ },
8193
8394
  bulkMoveFiles: {
8194
8395
  method: "POST",
8195
8396
  path: "/files/bulk/move",
@@ -8280,6 +8481,16 @@ var filesContract = c2.router({
8280
8481
  },
8281
8482
  summary: "List team files"
8282
8483
  },
8484
+ getFileTabCounts: {
8485
+ method: "GET",
8486
+ path: "/files/tab-counts",
8487
+ query: FileTabCountsQuerySchema,
8488
+ responses: {
8489
+ 200: FileTabCountsResponseSchema,
8490
+ 403: ErrorBodySchema
8491
+ },
8492
+ summary: "Per-tab asset counts for the Assets page, scoped to the search"
8493
+ },
8283
8494
  searchAgentAssets: {
8284
8495
  method: "GET",
8285
8496
  path: "/files/agent-search",
@@ -8290,6 +8501,15 @@ var filesContract = c2.router({
8290
8501
  },
8291
8502
  summary: "Search team assets for the agent"
8292
8503
  },
8504
+ listAgentAssetFolders: {
8505
+ method: "GET",
8506
+ path: "/files/agent-folders",
8507
+ responses: {
8508
+ 200: AgentListAssetFoldersResponseSchema,
8509
+ 403: ErrorBodySchema
8510
+ },
8511
+ summary: "List team asset folders for the agent"
8512
+ },
8293
8513
  getEmbeddingStorageUsage: {
8294
8514
  method: "GET",
8295
8515
  path: "/files/embedding-storage-usage",
@@ -8310,6 +8530,29 @@ var filesContract = c2.router({
8310
8530
  },
8311
8531
  summary: "Get a file's details with generation metadata"
8312
8532
  },
8533
+ getAssetShareMode: {
8534
+ method: "GET",
8535
+ path: "/files/:assetId/share-mode",
8536
+ pathParams: FileAssetIdParamsSchema,
8537
+ responses: {
8538
+ 200: external_exports.object({ shareMode: AssetShareModeSchema }),
8539
+ 403: ErrorBodySchema,
8540
+ 404: ErrorBodySchema
8541
+ },
8542
+ summary: "Get an asset's share mode"
8543
+ },
8544
+ updateAssetShareMode: {
8545
+ method: "PATCH",
8546
+ path: "/files/:assetId/share-mode",
8547
+ pathParams: FileAssetIdParamsSchema,
8548
+ body: external_exports.object({ shareMode: AssetShareModeSchema }),
8549
+ responses: {
8550
+ 200: external_exports.object({ shareMode: AssetShareModeSchema }),
8551
+ 403: ErrorBodySchema,
8552
+ 404: ErrorBodySchema
8553
+ },
8554
+ summary: "Set who can view an asset"
8555
+ },
8313
8556
  initFileUpload: {
8314
8557
  method: "POST",
8315
8558
  path: "/files/upload/init",
@@ -9006,38 +9249,44 @@ var AUTO_MODEL_REGISTRY = [
9006
9249
  {
9007
9250
  nodeType: "video",
9008
9251
  inputPattern: "text-only",
9009
- standard: { model: "kling-o3", variant: "text-to-video" },
9010
- fast: { model: "grok-imagine-video", variant: "text-to-video" }
9252
+ model: "kling-o3",
9253
+ variant: "text-to-video"
9011
9254
  },
9012
9255
  {
9013
9256
  nodeType: "video",
9014
9257
  inputPattern: "image-to-video",
9015
- standard: { model: "kling-o3", variant: "image-to-video" },
9016
- fast: { model: "wan-2.2", variant: "image-to-video" }
9258
+ model: "kling-o3",
9259
+ variant: "image-to-video"
9017
9260
  },
9018
9261
  {
9019
9262
  nodeType: "video",
9020
9263
  inputPattern: "first-last-frame-to-video",
9021
- standard: { model: "kling-o3", variant: "image-to-video" },
9022
- fast: { model: "kling-o3", variant: "image-to-video" }
9264
+ model: "kling-o3",
9265
+ variant: "image-to-video"
9023
9266
  },
9024
9267
  {
9025
9268
  nodeType: "video",
9026
9269
  inputPattern: "reference-to-video",
9027
- standard: { model: "kling-o3", variant: "reference-to-video" },
9028
- fast: { model: "kling-o3", variant: "reference-to-video" }
9270
+ model: "kling-o3",
9271
+ variant: "reference-to-video"
9029
9272
  },
9030
9273
  {
9031
9274
  nodeType: "video",
9032
9275
  inputPattern: "reference-with-frames-to-video",
9033
- standard: { model: "kling-o3", variant: "reference-to-video" },
9034
- fast: { model: "kling-o3", variant: "reference-to-video" }
9276
+ model: "kling-o3",
9277
+ variant: "reference-to-video"
9035
9278
  },
9036
9279
  {
9037
9280
  nodeType: "video",
9038
9281
  inputPattern: "video-with-audio",
9039
- standard: { model: "infinitalk", variant: "video-to-video" },
9040
- fast: { model: "infinitalk", variant: "video-to-video" }
9282
+ model: "infinitalk",
9283
+ variant: "video-to-video"
9284
+ },
9285
+ {
9286
+ nodeType: "video",
9287
+ inputPattern: "video-with-image-references",
9288
+ model: "kling-o3",
9289
+ variant: "video-to-video"
9041
9290
  },
9042
9291
  {
9043
9292
  nodeType: "video",
@@ -9047,85 +9296,79 @@ var AUTO_MODEL_REGISTRY = [
9047
9296
  // backend remaps reference → image_url at request build time
9048
9297
  // (see node-run-execution.ts → "primary image port" remap) since
9049
9298
  // kling-avatar's variant only accepts image_url.
9050
- standard: { model: "kling-avatar", variant: "image-to-video" },
9051
- fast: { model: "kling-avatar", variant: "image-to-video" }
9299
+ model: "kling-avatar",
9300
+ variant: "image-to-video"
9052
9301
  },
9053
9302
  {
9054
9303
  nodeType: "video",
9055
9304
  inputPattern: "frames-with-audio",
9056
9305
  // Kling Avatar does not accept end_image_url, so route frame+audio
9057
9306
  // runs through LTX's audio-capable image-to-video variant.
9058
- standard: { model: "ltx-2.3", variant: "image-to-video" },
9059
- fast: { model: "ltx-2.3", variant: "image-to-video" }
9307
+ model: "ltx-2.3",
9308
+ variant: "image-to-video"
9060
9309
  },
9061
9310
  {
9062
9311
  nodeType: "video",
9063
9312
  inputPattern: "video-to-video",
9064
- standard: { model: "grok-imagine-video", variant: "video-to-video" },
9065
- fast: { model: "grok-imagine-video", variant: "video-to-video" }
9313
+ model: "kling-o3",
9314
+ variant: "video-to-video"
9066
9315
  },
9067
9316
  // ── Image ───────────────────────────────────────────────────────────
9068
9317
  {
9069
9318
  nodeType: "image",
9070
9319
  inputPattern: "text-to-image",
9071
- standard: { model: "nano-banana-pro", variant: "text-to-image" },
9072
- fast: { model: "flux-schnell", variant: "text-to-image" }
9320
+ model: "nano-banana-pro",
9321
+ variant: "text-to-image"
9073
9322
  },
9074
9323
  {
9075
9324
  nodeType: "image",
9076
9325
  inputPattern: "image-to-image",
9077
- standard: { model: "nano-banana-pro", variant: "image-to-image" },
9078
- fast: { model: "grok-imagine", variant: "image-to-image" }
9326
+ model: "nano-banana-pro",
9327
+ variant: "image-to-image"
9079
9328
  },
9080
9329
  // ── Text ────────────────────────────────────────────────────────────
9081
9330
  {
9082
9331
  nodeType: "text",
9083
9332
  inputPattern: "default",
9084
- standard: { model: "gemini-3.1-pro", variant: "text" },
9085
- fast: { model: "gemini-3.1-pro", variant: "text" }
9333
+ model: "gemini-3.1-pro",
9334
+ variant: "text"
9086
9335
  },
9087
9336
  {
9088
9337
  nodeType: "text",
9089
9338
  inputPattern: "with-video",
9090
- standard: { model: "gemini-3.1-pro", variant: "text" },
9091
- fast: { model: "gemini-3.1-pro", variant: "text" }
9339
+ model: "gemini-3.1-pro",
9340
+ variant: "text"
9092
9341
  },
9093
9342
  // ── Audio ───────────────────────────────────────────────────────────
9094
9343
  {
9095
9344
  nodeType: "audio",
9096
9345
  inputPattern: "text-to-speech",
9097
- standard: { model: "eleven_v3", variant: "text-to-speech" },
9098
- fast: { model: "eleven_turbo_v2_5", variant: "text-to-speech" }
9346
+ model: "eleven_v3",
9347
+ variant: "text-to-speech"
9099
9348
  },
9100
9349
  {
9101
9350
  nodeType: "audio",
9102
9351
  inputPattern: "audio-to-audio",
9103
- standard: {
9104
- model: "elevenlabs-voice-changer",
9105
- variant: "audio-to-audio"
9106
- },
9107
- fast: {
9108
- model: "elevenlabs-voice-changer",
9109
- variant: "audio-to-audio"
9110
- }
9352
+ model: "elevenlabs-voice-changer",
9353
+ variant: "audio-to-audio"
9111
9354
  },
9112
9355
  {
9113
9356
  nodeType: "audio",
9114
9357
  inputPattern: "text-to-sfx",
9115
- standard: { model: "elevenlabs-sfx", variant: "text-to-sfx" },
9116
- fast: { model: "elevenlabs-sfx", variant: "text-to-sfx" }
9358
+ model: "elevenlabs-sfx",
9359
+ variant: "text-to-sfx"
9117
9360
  },
9118
9361
  {
9119
9362
  nodeType: "audio",
9120
9363
  inputPattern: "text-to-music",
9121
- standard: { model: "elevenlabs-music", variant: "text-to-music" },
9122
- fast: { model: "elevenlabs-music", variant: "text-to-music" }
9364
+ model: "elevenlabs-music",
9365
+ variant: "text-to-music"
9123
9366
  },
9124
9367
  {
9125
9368
  nodeType: "audio",
9126
9369
  inputPattern: "speech-to-text",
9127
- standard: { model: "scribe_v2", variant: "speech-to-text" },
9128
- fast: { model: "scribe_v2", variant: "speech-to-text" }
9370
+ model: "scribe_v2",
9371
+ variant: "speech-to-text"
9129
9372
  }
9130
9373
  ];
9131
9374
  var AutoModelProxy = class {
@@ -9149,15 +9392,14 @@ var AutoModelProxy = class {
9149
9392
  case "video":
9150
9393
  return this.resolveVideo(
9151
9394
  input.connectedInputs,
9152
- input.tier,
9153
9395
  input.canvasVideoInputRoutingEnabled === true
9154
9396
  );
9155
9397
  case "image":
9156
- return this.resolveImage(input.connectedInputs, input.tier);
9398
+ return this.resolveImage(input.connectedInputs);
9157
9399
  case "text":
9158
- return this.resolveText(input.connectedInputs, input.tier);
9400
+ return this.resolveText(input.connectedInputs);
9159
9401
  case "audio":
9160
- return this.resolveAudio(input.variant, input.tier);
9402
+ return this.resolveAudio(input.variant);
9161
9403
  default:
9162
9404
  return {
9163
9405
  valid: false,
@@ -9170,13 +9412,22 @@ var AutoModelProxy = class {
9170
9412
  return this.registry;
9171
9413
  }
9172
9414
  // ── Private resolvers ───────────────────────────────────────────────
9173
- resolveVideo(ci, tier, canvasVideoInputRoutingEnabled) {
9415
+ resolveVideo(ci, canvasVideoInputRoutingEnabled) {
9174
9416
  const hasAnyImage = ci.hasImage || ci.hasStartingImage || ci.hasEndImage || ci.hasReferenceImage;
9175
9417
  if (ci.hasVideo && hasAnyImage) {
9176
- return {
9177
- valid: false,
9178
- errorMessage: "You cannot have an image and a video as input at the same time"
9179
- };
9418
+ if (ci.hasEndImage) {
9419
+ return {
9420
+ valid: false,
9421
+ errorMessage: "A last frame cannot be used with a video input"
9422
+ };
9423
+ }
9424
+ if (ci.hasAudio) {
9425
+ return {
9426
+ valid: false,
9427
+ errorMessage: "Audio input is not supported with image and video inputs together"
9428
+ };
9429
+ }
9430
+ return this.lookupRoute("video", "video-with-image-references");
9180
9431
  }
9181
9432
  if (ci.hasEndImage && !ci.hasStartingImage && !ci.hasReferenceImage && !ci.hasImage && !ci.hasVideo) {
9182
9433
  return {
@@ -9191,17 +9442,17 @@ var AutoModelProxy = class {
9191
9442
  };
9192
9443
  }
9193
9444
  if (ci.hasAudio && ci.hasVideo) {
9194
- return this.lookupRoute("video", "video-with-audio", tier);
9445
+ return this.lookupRoute("video", "video-with-audio");
9195
9446
  }
9196
9447
  if (ci.hasAudio && (ci.hasStartingImage || ci.hasImage) && ci.hasEndImage) {
9197
- return canvasVideoInputRoutingEnabled ? this.lookupRoute("video", "frames-with-audio", tier) : {
9448
+ return canvasVideoInputRoutingEnabled ? this.lookupRoute("video", "frames-with-audio") : {
9198
9449
  valid: true,
9199
9450
  model: "ltx-2.3-audio",
9200
9451
  variant: "image-to-video"
9201
9452
  };
9202
9453
  }
9203
9454
  if (ci.hasAudio && hasAnyImage) {
9204
- return this.lookupRoute("video", "image-with-audio", tier);
9455
+ return this.lookupRoute("video", "image-with-audio");
9205
9456
  }
9206
9457
  if (ci.hasAudio) {
9207
9458
  return {
@@ -9210,48 +9461,40 @@ var AutoModelProxy = class {
9210
9461
  };
9211
9462
  }
9212
9463
  if (ci.hasVideo) {
9213
- return this.lookupRoute("video", "video-to-video", tier);
9464
+ return this.lookupRoute("video", "video-to-video");
9214
9465
  }
9215
9466
  const hasFrameInput = ci.hasStartingImage || ci.hasEndImage || ci.hasImage;
9216
9467
  if (ci.hasReferenceImage && hasFrameInput) {
9217
- return this.lookupRoute(
9218
- "video",
9219
- "reference-with-frames-to-video",
9220
- tier
9221
- );
9468
+ return this.lookupRoute("video", "reference-with-frames-to-video");
9222
9469
  }
9223
9470
  if (ci.hasReferenceImage) {
9224
- return this.lookupRoute("video", "reference-to-video", tier);
9471
+ return this.lookupRoute("video", "reference-to-video");
9225
9472
  }
9226
9473
  if (ci.hasStartingImage || ci.hasImage) {
9227
9474
  if (ci.hasEndImage) {
9228
- return this.lookupRoute(
9229
- "video",
9230
- "first-last-frame-to-video",
9231
- tier
9232
- );
9475
+ return this.lookupRoute("video", "first-last-frame-to-video");
9233
9476
  }
9234
- return this.lookupRoute("video", "image-to-video", tier);
9477
+ return this.lookupRoute("video", "image-to-video");
9235
9478
  }
9236
- return this.lookupRoute("video", "text-only", tier);
9479
+ return this.lookupRoute("video", "text-only");
9237
9480
  }
9238
- resolveImage(ci, tier) {
9481
+ resolveImage(ci) {
9239
9482
  if (ci.hasImage || ci.hasReferenceImage) {
9240
- return this.lookupRoute("image", "image-to-image", tier);
9483
+ return this.lookupRoute("image", "image-to-image");
9241
9484
  }
9242
- return this.lookupRoute("image", "text-to-image", tier);
9485
+ return this.lookupRoute("image", "text-to-image");
9243
9486
  }
9244
- resolveAudio(variant, tier) {
9487
+ resolveAudio(variant) {
9245
9488
  const pattern = variant ?? "text-to-speech";
9246
- return this.lookupRoute("audio", pattern, tier);
9489
+ return this.lookupRoute("audio", pattern);
9247
9490
  }
9248
- resolveText(ci, tier) {
9491
+ resolveText(ci) {
9249
9492
  if (ci.hasVideo) {
9250
- return this.lookupRoute("text", "with-video", tier);
9493
+ return this.lookupRoute("text", "with-video");
9251
9494
  }
9252
- return this.lookupRoute("text", "default", tier);
9495
+ return this.lookupRoute("text", "default");
9253
9496
  }
9254
- lookupRoute(nodeType, inputPattern, tier) {
9497
+ lookupRoute(nodeType, inputPattern) {
9255
9498
  const route = this.lookup.get(`${nodeType}:${inputPattern}`);
9256
9499
  if (!route) {
9257
9500
  return {
@@ -9259,8 +9502,7 @@ var AutoModelProxy = class {
9259
9502
  errorMessage: `No auto model route for ${nodeType}:${inputPattern}`
9260
9503
  };
9261
9504
  }
9262
- const entry = tier === "fast" ? route.fast : route.standard;
9263
- return { valid: true, model: entry.model, variant: entry.variant };
9505
+ return { valid: true, model: route.model, variant: route.variant };
9264
9506
  }
9265
9507
  };
9266
9508
 
@@ -9295,6 +9537,19 @@ var AUDIO_NODE_WIDTH = 380;
9295
9537
  var AUDIO_NODE_ESTIMATED_HEIGHT = 420;
9296
9538
  var PDF_NODE_DEFAULT_WIDTH = 440;
9297
9539
  var NODE_CARD_OVERHEAD = 252;
9540
+ var ASPECT_RATIOS = [
9541
+ "21:9",
9542
+ "16:9",
9543
+ "3:2",
9544
+ "4:3",
9545
+ "5:4",
9546
+ "1:1",
9547
+ "4:5",
9548
+ "3:4",
9549
+ "2:3",
9550
+ "9:16",
9551
+ "9:21"
9552
+ ];
9298
9553
  var MODALITIES = {
9299
9554
  text: { modelCategory: "text" },
9300
9555
  image: { modelCategory: "image" },
@@ -10038,7 +10293,8 @@ var TemplatePortSummarySchema = external_exports.object({
10038
10293
  label: external_exports.string(),
10039
10294
  handleType: external_exports.string(),
10040
10295
  required: external_exports.boolean().optional(),
10041
- maxConnections: external_exports.number().int().positive().optional()
10296
+ maxConnections: external_exports.number().int().positive().optional(),
10297
+ outputAspectRatio: external_exports.enum(ASPECT_RATIOS).nullable().optional()
10042
10298
  });
10043
10299
 
10044
10300
  // ../api-contract/src/canvas.ts
@@ -10088,6 +10344,8 @@ var ModelConfigSchema = external_exports.object({
10088
10344
  model: external_exports.string(),
10089
10345
  variant: external_exports.string(),
10090
10346
  tier: external_exports.string().nullable().optional(),
10347
+ /** HeyGen Avatar V only: the Runware avatar id for this node. */
10348
+ heygenAvatar: external_exports.string().optional(),
10091
10349
  // Magic-resize stores its per-run config here so each version carries the
10092
10350
  // ratios + resolution that produced it.
10093
10351
  ratios: external_exports.array(external_exports.string()).optional(),
@@ -10112,25 +10370,15 @@ var ModelConfigInputSchema = external_exports.object({
10112
10370
  model: external_exports.string(),
10113
10371
  variant: external_exports.string().refine(isValidGenerationMode, "Invalid generation mode"),
10114
10372
  tier: external_exports.string().nullable().optional(),
10373
+ /** HeyGen Avatar V only: the Runware avatar id for this node. */
10374
+ heygenAvatar: external_exports.string().trim().min(1).optional(),
10115
10375
  /** better-font-32b only: the font the model renders the requested text in (supplied to the model as a rendered reference image). */
10116
10376
  font: external_exports.object({
10117
10377
  source: external_exports.enum(["custom", "google"]),
10118
10378
  familyName: external_exports.string()
10119
10379
  }).optional()
10120
10380
  });
10121
- var OutputAspectRatioSchema = external_exports.enum([
10122
- "21:9",
10123
- "16:9",
10124
- "4:3",
10125
- "5:4",
10126
- "3:2",
10127
- "1:1",
10128
- "2:3",
10129
- "3:4",
10130
- "4:5",
10131
- "9:16",
10132
- "9:21"
10133
- ]);
10381
+ var OutputAspectRatioSchema = external_exports.enum(ASPECT_RATIOS);
10134
10382
  var OutputResolutionPresetSchema = external_exports.enum([
10135
10383
  "360p",
10136
10384
  "480p",
@@ -10165,9 +10413,10 @@ var CropRectSchema = external_exports.object({
10165
10413
  });
10166
10414
  var StitchClipOrderSchema = external_exports.array(external_exports.string());
10167
10415
  var StitchScaleFitSchema = external_exports.enum(["fit", "stretch"]);
10416
+ var roundMs = (value) => typeof value === "number" ? Math.round(value) : value;
10168
10417
  var StitchTrimWindowFields = {
10169
- trimStartMs: external_exports.number().int().nonnegative().optional(),
10170
- trimEndMs: external_exports.number().int().positive().optional()
10418
+ trimStartMs: external_exports.preprocess(roundMs, external_exports.number().int().nonnegative()).optional(),
10419
+ trimEndMs: external_exports.preprocess(roundMs, external_exports.number().int().positive()).optional()
10171
10420
  };
10172
10421
  var StitchAudioOverlaySchema = external_exports.object({
10173
10422
  nodeId: external_exports.string(),
@@ -12038,6 +12287,8 @@ var GenerationRunBodySchema = external_exports.object({
12038
12287
  model: external_exports.string().trim().min(1),
12039
12288
  tier: external_exports.string().optional(),
12040
12289
  prompt: external_exports.string().trim().min(1).optional(),
12290
+ /** Selected avatar id from Runware's HeyGen Avatar V catalog. */
12291
+ heygenAvatar: external_exports.string().trim().min(1).optional(),
12041
12292
  imageUrls: external_exports.array(external_exports.string().url()).optional(),
12042
12293
  endImageUrls: external_exports.array(external_exports.string().url()).optional(),
12043
12294
  referenceImageUrls: external_exports.array(external_exports.string().url()).optional(),
@@ -12202,6 +12453,10 @@ var ModelCostSchema = external_exports.discriminatedUnion("type", [
12202
12453
  maxCreditCost: external_exports.number()
12203
12454
  })
12204
12455
  ]);
12456
+ var TextTokenPricingSchema = external_exports.object({
12457
+ inputCreditsPerMillionTokens: external_exports.number(),
12458
+ outputCreditsPerMillionTokens: external_exports.number()
12459
+ });
12205
12460
  var InputFileSizeConstraintsResponseSchema = external_exports.object({
12206
12461
  maximumFileBytes: external_exports.number().int().nonnegative().optional(),
12207
12462
  maximumTotalBytes: external_exports.number().int().nonnegative().optional()
@@ -12307,7 +12562,9 @@ var ModelResponseSchema = external_exports.object({
12307
12562
  hiddenFromAgent: external_exports.boolean().optional(),
12308
12563
  promoRateLimit: PromoRateLimitSchema.optional(),
12309
12564
  /** Maximum prompt length in characters. Used by text models that lack variants. */
12310
- maxPromptLength: external_exports.number().nullable().optional()
12565
+ maxPromptLength: external_exports.number().nullable().optional(),
12566
+ /** Per-1M-token credit rates for text models; drives the live cost-range estimate. */
12567
+ textTokenPricing: TextTokenPricingSchema.optional()
12311
12568
  });
12312
12569
  var ListGenerativeModelsResponseSchema = external_exports.object({
12313
12570
  models: external_exports.array(ModelResponseSchema)
@@ -12374,6 +12631,13 @@ var generationContract = c9.router({
12374
12631
  200: ListGenerativeModelsResponseSchema
12375
12632
  }
12376
12633
  },
12634
+ listHeygenAvatarVAvatars: {
12635
+ method: "GET",
12636
+ path: "/generation/heygen-avatar-v-avatars",
12637
+ responses: {
12638
+ 200: external_exports.object({ avatars: external_exports.array(external_exports.string()) })
12639
+ }
12640
+ },
12377
12641
  getAutoModelDefaults: {
12378
12642
  method: "GET",
12379
12643
  path: "/generation/auto-model-defaults",
@@ -12659,6 +12923,17 @@ var profileContract = c11.router({
12659
12923
  // ../api-contract/src/share.ts
12660
12924
  var c12 = initContract();
12661
12925
  var ShareModeSchema = external_exports.enum(["restricted", "view"]);
12926
+ var SharedAssetResponseSchema = external_exports.object({
12927
+ id: external_exports.string().uuid(),
12928
+ filename: external_exports.string(),
12929
+ fileType: FileTypeSchema,
12930
+ contentType: external_exports.string().nullable(),
12931
+ // Presigned media URL; null only when the underlying storage is missing.
12932
+ mediaUrl: external_exports.string().nullable(),
12933
+ width: external_exports.number().int().nullable(),
12934
+ height: external_exports.number().int().nullable(),
12935
+ durationInMs: external_exports.number().int().nullable()
12936
+ });
12662
12937
  var ShareCanvasResponseSchema = external_exports.object({
12663
12938
  id: external_exports.string().uuid(),
12664
12939
  projectId: external_exports.string().uuid(),
@@ -12718,6 +12993,16 @@ var shareContract = c12.router({
12718
12993
  }
12719
12994
  },
12720
12995
  // Public endpoint — no auth required
12996
+ getSharedAsset: {
12997
+ method: "GET",
12998
+ path: "/share/assets/:assetId",
12999
+ pathParams: external_exports.object({ assetId: external_exports.string().uuid() }),
13000
+ responses: {
13001
+ 200: SharedAssetResponseSchema,
13002
+ 404: MessageResponseSchema
13003
+ }
13004
+ },
13005
+ // Public endpoint — no auth required
12721
13006
  getSharedCanvas: {
12722
13007
  method: "GET",
12723
13008
  path: "/share/projects/:projectId/canvases/:canvasId",
@@ -12800,7 +13085,8 @@ var EnterpriseOrderLineItemSchema = external_exports.object({
12800
13085
  var EnterpriseBillingIntervalSchema = external_exports.enum(["monthly", "annual"]);
12801
13086
  var EnterpriseStripeBillingInputSchema = external_exports.object({
12802
13087
  unitAmount: external_exports.number().int().positive(),
12803
- currency: external_exports.string().length(3).transform((value) => value.toLowerCase()),
13088
+ // Expose the transform output to OpenAPI generation.
13089
+ currency: external_exports.string().length(3).transform((value) => value.toLowerCase()).pipe(external_exports.string()),
12804
13090
  billingInterval: EnterpriseBillingIntervalSchema,
12805
13091
  creditsPerPeriod: external_exports.number().int().positive(),
12806
13092
  contractEndAt: external_exports.string().datetime().optional()
@@ -12864,7 +13150,8 @@ var EnterprisePendingExpansionSchema = external_exports.object({
12864
13150
  });
12865
13151
  var EnterpriseStripeBillingSchema = external_exports.object({
12866
13152
  unitAmount: external_exports.number().int().positive(),
12867
- currency: external_exports.string().length(3).transform((value) => value.toLowerCase()),
13153
+ // Expose the transform output to OpenAPI generation.
13154
+ currency: external_exports.string().length(3).transform((value) => value.toLowerCase()).pipe(external_exports.string()),
12868
13155
  billingInterval: EnterpriseBillingIntervalSchema,
12869
13156
  creditsPerPeriod: external_exports.number().int().positive(),
12870
13157
  contractEndAt: external_exports.string().datetime().optional(),
@@ -13578,10 +13865,13 @@ var publicContract = c16.router(
13578
13865
  complete: uploadContract.complete
13579
13866
  },
13580
13867
  asset: {
13581
- // Semantic/filename search of the team's Files (DAM) library, and
13582
- // placing a found asset onto a canvas by reference (no duplicate).
13583
- // Both are gated to Pro + Enterprise by the handlers.
13868
+ // Semantic/filename search of the team's Files (DAM) library,
13869
+ // listing the library's folders (to resolve a folder name id for a
13870
+ // folder-scoped search), and placing a found asset onto a canvas by
13871
+ // reference (no duplicate). All gated to Pro + Enterprise by the
13872
+ // handlers.
13584
13873
  search: filesContract.searchAgentAssets,
13874
+ folders: filesContract.listAgentAssetFolders,
13585
13875
  place: canvasContract.placeLibraryAssets
13586
13876
  },
13587
13877
  model: {
@@ -13848,7 +14138,7 @@ function isMajorUpgrade(current, latest) {
13848
14138
  return l[0] > c17[0];
13849
14139
  }
13850
14140
  function shouldShowUpgradeNotice(ctx = {}) {
13851
- const current = ctx.current ?? "0.6.0";
14141
+ const current = ctx.current ?? "0.8.0";
13852
14142
  const latest = ctx.latest ?? serverLatestVersion;
13853
14143
  const env = ctx.env ?? process.env;
13854
14144
  const isTty = ctx.isTty ?? Boolean(process.stderr.isTTY);
@@ -13869,7 +14159,7 @@ Upgrade: npm install -g @melius-ai/cli@latest (or: brew update && brew upgrade
13869
14159
  function printUpgradeNoticeIfNeeded(ctx = {}) {
13870
14160
  try {
13871
14161
  if (!shouldShowUpgradeNotice(ctx)) return;
13872
- const current = ctx.current ?? "0.6.0";
14162
+ const current = ctx.current ?? "0.8.0";
13873
14163
  const latest = ctx.latest ?? serverLatestVersion;
13874
14164
  process.stderr.write(formatUpgradeNotice(current, latest));
13875
14165
  } catch {
@@ -18553,10 +18843,10 @@ function registerAssetCommands(program2, getOutputOpts) {
18553
18843
  "Search and place the team's Files (DAM) library assets. Requires a Pro or Enterprise plan"
18554
18844
  );
18555
18845
  asset.command("search").description(
18556
- "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"
18846
+ "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; filter by tag with --tags; omit the query and pass --folder-id or --tags to bring in the whole folder / all tagged assets"
18557
18847
  ).argument(
18558
- "<query>",
18559
- 'Search terms \u2014 a filename or a description of the content, e.g. "Product_Shot_05" or "black jacket shot"'
18848
+ "[query]",
18849
+ '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'
18560
18850
  ).option("--limit <n>", "Max results per page (1-20, default 20)", "20").option(
18561
18851
  "--offset <n>",
18562
18852
  "Skip the first N matches to page deeper (default 0). Page with --limit until the running count reaches the response's total",
@@ -18567,23 +18857,46 @@ function registerAssetCommands(program2, getOutputOpts) {
18567
18857
  ).option(
18568
18858
  "--canvas-id <canvasId>",
18569
18859
  "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)"
18860
+ ).option(
18861
+ "--folder-id <folderId>",
18862
+ "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"
18863
+ ).option(
18864
+ "--tags <names>",
18865
+ "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"
18570
18866
  ).addHelpText(
18571
18867
  "after",
18572
18868
  `
18573
18869
  Returns: { query, total, results: [{ assetId, filename, displayName, fileType, contentType, previewUrl, assetUrl, durationInMs }] }.
18574
18870
  total is the full match count; page through it with --limit + --offset.
18575
18871
  Use the returned assetId values with mel asset place to bring them onto a canvas.
18872
+ Resolve a folder name to an id with mel asset folders first.
18576
18873
 
18577
18874
  Examples:
18578
18875
  $ mel asset search "black jacket shot"
18579
18876
  $ mel asset search "logo" --file-type image --limit 10
18580
- $ mel asset search "logo" --limit 20 --offset 20`
18877
+ $ mel asset search "logo" --limit 20 --offset 20
18878
+ $ mel asset search "shark" --folder-id <folderId> # find within a folder
18879
+ $ mel asset search --folder-id <folderId> # bring in the WHOLE folder
18880
+ $ mel asset search "logo" --tags brand # find tagged assets
18881
+ $ mel asset search --tags brand,hero # every asset with BOTH tags`
18581
18882
  ).action(
18582
18883
  async (query, opts) => {
18884
+ if (query === void 0 && opts.folderId === void 0 && opts.tags === void 0) {
18885
+ writeError(
18886
+ formatError(
18887
+ "USAGE_ERROR",
18888
+ "Provide a search query, --folder-id to bring in a whole folder, or --tags to bring in tagged assets",
18889
+ {
18890
+ suggestion: "Run mel asset folders to list folder ids, then: mel asset search --folder-id <folderId> (or: mel asset search --tags <name>)"
18891
+ }
18892
+ ),
18893
+ EXIT_USAGE_ERROR
18894
+ );
18895
+ }
18583
18896
  const client = getClient();
18584
18897
  const result = await client.asset.search({
18585
18898
  query: {
18586
- query,
18899
+ ...query !== void 0 && { query },
18587
18900
  limit: Number(opts.limit),
18588
18901
  offset: Number(opts.offset),
18589
18902
  ...opts.fileType !== void 0 && {
@@ -18591,7 +18904,11 @@ Examples:
18591
18904
  },
18592
18905
  ...opts.canvasId !== void 0 && {
18593
18906
  canvasId: opts.canvasId
18594
- }
18907
+ },
18908
+ ...opts.folderId !== void 0 && {
18909
+ folderId: opts.folderId
18910
+ },
18911
+ ...opts.tags !== void 0 && { tags: opts.tags }
18595
18912
  }
18596
18913
  });
18597
18914
  if (result.status !== 200) {
@@ -18604,6 +18921,31 @@ Examples:
18604
18921
  writeJson(result.body, getOutputOpts());
18605
18922
  }
18606
18923
  );
18924
+ asset.command("folders").description(
18925
+ "List the team's Files/DAM folders (id, name, full path, file count). Use a folder's id with mel asset search --folder-id to scope a search or bring in the whole folder"
18926
+ ).addHelpText(
18927
+ "after",
18928
+ `
18929
+ Returns: an array of { id, name, path, fileCount }.
18930
+ path is the folder's location from the library root (e.g. "Brand / Logos") \u2014 use it to disambiguate same-named folders.
18931
+ Pass a folder id to mel asset search --folder-id <folderId>.
18932
+
18933
+ Examples:
18934
+ $ mel asset folders
18935
+ $ mel asset folders --text`
18936
+ ).action(async () => {
18937
+ const client = getClient();
18938
+ const result = await client.asset.folders();
18939
+ if (result.status !== 200) {
18940
+ handleApiError(
18941
+ result.status,
18942
+ result.body,
18943
+ result.status === 403 ? ASSET_PLAN_REQUIRED_SUGGESTION : void 0
18944
+ );
18945
+ }
18946
+ const body = result.body;
18947
+ writeJson(body.folders, getOutputOpts());
18948
+ });
18607
18949
  asset.command("place").description(
18608
18950
  "Place existing library assets onto a canvas by id, referencing the ORIGINAL asset (no duplicate, no re-upload). Creates file nodes server-side (writes to CRDT) and returns their node ids"
18609
18951
  ).argument(
@@ -18773,7 +19115,7 @@ function withDefaultHelp(args) {
18773
19115
  }
18774
19116
  function createProgram() {
18775
19117
  const program2 = new Command();
18776
- program2.name("mel").description("Mel \u2014 Melius agent CLI").version("0.6.0").showSuggestionAfterError(true).option("--json", "Force JSON output (default)").option("--text", "Human-readable output").option(
19118
+ program2.name("mel").description("Mel \u2014 Melius agent CLI").version("0.8.0").showSuggestionAfterError(true).option("--json", "Force JSON output (default)").option("--text", "Human-readable output").option(
18777
19119
  "--fields <fields>",
18778
19120
  "Select specific output fields (comma-separated)"
18779
19121
  ).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-EN744KKV.js";
5
+ } from "../chunk-3PBGQVUH.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.6.0",
3
+ "version": "0.8.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",