@melius-ai/cli 0.5.2 → 0.7.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
@@ -259,12 +259,15 @@ mel upload ./photo.png --node-id <nodeId> --canvas-id <canvasId>
259
259
 
260
260
  Requires the Pro or Enterprise plan.
261
261
 
262
- | Command | Description |
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 5 matches |
265
- | `mel asset place <canvasId> <assetIds...>` | Place existing library assets onto a canvas by id references the **original** asset (no duplicate) |
262
+ | Command | Description |
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 |
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
+ | `mel asset place <canvasId> <assetIds...>` | Place existing library assets onto a canvas by id — references the **original** asset (no duplicate) |
266
267
 
267
- `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-5>`; 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. 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).
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.
268
271
 
269
272
  `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
273
 
@@ -272,6 +275,11 @@ Requires the Pro or Enterprise plan.
272
275
  # Find an asset, then bring it onto a canvas and wire it
273
276
  mel asset search "black jacket shot" --file-type image
274
277
  mel asset place <canvasId> <assetId1> <assetId2>
278
+
279
+ # Bring in a whole folder: resolve the folder, then search it with no query
280
+ mel asset folders # → [{ id, name, path, fileCount }]
281
+ mel asset search --folder-id <folderId> # every asset in the folder
282
+ mel asset search "shark" --folder-id <folderId> # or filter within it
275
283
  ```
276
284
 
277
285
  ### 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-4GUG5TD2.js";
10
+ } from "../chunk-5QT6WMID.js";
11
11
 
12
12
  // bin/mel.ts
13
13
  var program = createProgram();
@@ -7914,7 +7914,11 @@ var FileTypeSchema = external_exports.enum([
7914
7914
  var FileCanvasBacklinkSchema = external_exports.object({
7915
7915
  canvasId: external_exports.string().uuid(),
7916
7916
  projectId: external_exports.string().uuid(),
7917
- canvasTitle: external_exports.string()
7917
+ canvasTitle: external_exports.string(),
7918
+ // A node on this canvas whose active version holds the asset, so opening
7919
+ // the canvas can center on it. Null when no such node survives (e.g. it was
7920
+ // deleted but the asset is still referenced elsewhere on the canvas).
7921
+ nodeId: external_exports.string().uuid().nullable().optional()
7918
7922
  });
7919
7923
  var FileCardSchema = external_exports.object({
7920
7924
  assetId: external_exports.string().uuid(),
@@ -8079,14 +8083,20 @@ var ListFilesResponseSchema = external_exports.object({
8079
8083
  cursor: external_exports.string().nullable().optional(),
8080
8084
  hasMore: external_exports.boolean().optional()
8081
8085
  });
8086
+ var AGENT_ASSET_SEARCH_PAGE_SIZE = 20;
8082
8087
  var AgentAssetSearchQuerySchema = external_exports.object({
8083
- query: external_exports.string().trim().min(1).max(500).describe("Asset search query"),
8084
- limit: external_exports.coerce.number().int().min(1).max(5).default(5),
8088
+ 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`.'
8090
+ ),
8091
+ limit: external_exports.coerce.number().int().min(1).max(AGENT_ASSET_SEARCH_PAGE_SIZE).default(AGENT_ASSET_SEARCH_PAGE_SIZE),
8085
8092
  source: FileSourceSchema.optional(),
8086
8093
  fileType: FileTypeSchema.optional(),
8087
8094
  canvasId: external_exports.string().uuid().optional().describe(
8088
8095
  '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.'
8089
8096
  ),
8097
+ 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.'
8099
+ ),
8090
8100
  showInThread: external_exports.boolean().optional().describe(
8091
8101
  '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.'
8092
8102
  )
@@ -8094,13 +8104,40 @@ var AgentAssetSearchQuerySchema = external_exports.object({
8094
8104
  var AgentAssetSearchModeSchema = external_exports.enum(["semantic", "filename"]);
8095
8105
  var AgentAssetSearchRequestSchema = AgentAssetSearchQuerySchema.extend(
8096
8106
  {
8097
- mode: AgentAssetSearchModeSchema.optional()
8107
+ mode: AgentAssetSearchModeSchema.optional(),
8108
+ // Pagination offset for the picker's "More results" button. Like
8109
+ // `mode`, it lives here and NOT on `AgentAssetSearchQuerySchema` (the
8110
+ // LLM tool input) so the model can never paginate — paging is a member
8111
+ // action. Optional (not `.default(0)`): a default would make `offset`
8112
+ // required in the inferred type and break assignment from the tool
8113
+ // input, which has no offset. The service treats an absent offset as 0.
8114
+ offset: external_exports.coerce.number().int().min(0).optional()
8098
8115
  }
8099
8116
  );
8100
8117
  var AgentAssetSearchResponseSchema = external_exports.object({
8101
8118
  query: external_exports.string(),
8102
8119
  results: external_exports.array(FileCardSchema),
8103
- total: external_exports.number().int().nonnegative()
8120
+ total: external_exports.number().int().nonnegative(),
8121
+ // Echo the search scope so the picker's "More results" button can re-page
8122
+ // with the SAME scope. Without this, page 2 drops the folder/canvas filter
8123
+ // and pages the whole library (and a query-less folder browse can't page at
8124
+ // all). Absent when the corresponding filter wasn't set.
8125
+ folderId: external_exports.string().uuid().optional(),
8126
+ canvasId: external_exports.string().uuid().optional(),
8127
+ source: FileSourceSchema.optional(),
8128
+ fileType: FileTypeSchema.optional()
8129
+ });
8130
+ var AgentListAssetFoldersQuerySchema = external_exports.object({});
8131
+ var AgentAssetFolderSchema = external_exports.object({
8132
+ id: external_exports.string().uuid(),
8133
+ name: external_exports.string(),
8134
+ // Full path from the team root (e.g. "Brand / Logos") so the agent can
8135
+ // disambiguate when several folders share a name.
8136
+ path: external_exports.string(),
8137
+ fileCount: external_exports.number().int().nonnegative()
8138
+ });
8139
+ var AgentListAssetFoldersResponseSchema = external_exports.object({
8140
+ folders: external_exports.array(AgentAssetFolderSchema)
8104
8141
  });
8105
8142
  var InitFilesUploadBodySchema = external_exports.object({
8106
8143
  filename: external_exports.string(),
@@ -8278,6 +8315,15 @@ var filesContract = c2.router({
8278
8315
  },
8279
8316
  summary: "Search team assets for the agent"
8280
8317
  },
8318
+ listAgentAssetFolders: {
8319
+ method: "GET",
8320
+ path: "/files/agent-folders",
8321
+ responses: {
8322
+ 200: AgentListAssetFoldersResponseSchema,
8323
+ 403: ErrorBodySchema
8324
+ },
8325
+ summary: "List team asset folders for the agent"
8326
+ },
8281
8327
  getEmbeddingStorageUsage: {
8282
8328
  method: "GET",
8283
8329
  path: "/files/embedding-storage-usage",
@@ -8703,7 +8749,15 @@ var agentContract = c3.router({
8703
8749
  // clients that predate selection persistence.
8704
8750
  assetIds: external_exports.array(external_exports.string()).optional()
8705
8751
  }),
8706
- external_exports.object({ action: external_exports.literal("decline") })
8752
+ external_exports.object({
8753
+ action: external_exports.literal("decline"),
8754
+ // When the member declines these results and instead types a new
8755
+ // query into the card's "search for something else" input, the
8756
+ // resumed turn re-runs asset_search with exactly this query
8757
+ // instead of the agent asking a clarify. Bounds mirror
8758
+ // `AgentAssetSearchQuerySchema.query`. Omitted = plain decline.
8759
+ searchAgainQuery: external_exports.string().trim().min(1).max(500).optional()
8760
+ })
8707
8761
  ]),
8708
8762
  responses: {
8709
8763
  200: AgentToolApprovalResponseSchema,
@@ -9061,14 +9115,14 @@ var AUTO_MODEL_REGISTRY = [
9061
9115
  {
9062
9116
  nodeType: "text",
9063
9117
  inputPattern: "default",
9064
- standard: { model: "gemini-3-flash", variant: "text" },
9065
- fast: { model: "gemini-3-flash", variant: "text" }
9118
+ standard: { model: "gemini-3.1-pro", variant: "text" },
9119
+ fast: { model: "gemini-3.1-pro", variant: "text" }
9066
9120
  },
9067
9121
  {
9068
9122
  nodeType: "text",
9069
9123
  inputPattern: "with-video",
9070
- standard: { model: "gemini-3-flash", variant: "text" },
9071
- fast: { model: "gemini-3-flash", variant: "text" }
9124
+ standard: { model: "gemini-3.1-pro", variant: "text" },
9125
+ fast: { model: "gemini-3.1-pro", variant: "text" }
9072
9126
  },
9073
9127
  // ── Audio ───────────────────────────────────────────────────────────
9074
9128
  {
@@ -9928,15 +9982,22 @@ var TemplatePortSchema = external_exports.discriminatedUnion("direction", [
9928
9982
  /** Creator-set cap on connections into this input. Absent = unlimited. */
9929
9983
  maxConnections: external_exports.number().int().positive().optional(),
9930
9984
  /**
9931
- * Hidden input: not exposed as a connectable handle on the collapsed box
9985
+ * Locked input: not exposed as a connectable handle on the collapsed box
9932
9986
  * — its value comes from the source baked into the template definition
9933
9987
  * (used as-is for an upload/custom text, regenerated for a generation
9934
9988
  * node). The port is still recorded (rather than dropped) so it
9935
- * round-trips for editing in the internal tool. At apply, hidden input
9989
+ * round-trips for editing in the internal tool. At apply, locked input
9936
9990
  * ports are not materialized as instance bindings, so no handle renders
9937
9991
  * and they never gate a run.
9938
9992
  */
9939
- hidden: external_exports.boolean().optional(),
9993
+ locked: external_exports.boolean().optional(),
9994
+ /**
9995
+ * For a locked input only: the template node key of the baked source
9996
+ * whose value is frozen in. Lets apply resolve the materialized source
9997
+ * node so "Expand template" can surface it into the input column.
9998
+ * Absent for visible inputs (the user supplies their own source).
9999
+ */
10000
+ sourceNodeKey: external_exports.string().optional(),
9940
10001
  /**
9941
10002
  * Every internal handle this input fans out to. Absent/empty = the
9942
10003
  * single `nodeKey`/`internalHandle` binding (pre-fan-out templates).
@@ -9971,12 +10032,12 @@ var InstancePortBindingSchema = external_exports.discriminatedUnion("direction",
9971
10032
  required: external_exports.boolean().optional(),
9972
10033
  maxConnections: external_exports.number().int().positive().optional(),
9973
10034
  /**
9974
- * Mirror of `TemplatePort.hidden`. Hidden input ports are not
10035
+ * Mirror of `TemplatePort.locked`. Locked input ports are not
9975
10036
  * materialized as instance bindings today (the box never renders them),
9976
10037
  * so this is carried for symmetry/forward-compat rather than read at
9977
10038
  * runtime.
9978
10039
  */
9979
- hidden: external_exports.boolean().optional(),
10040
+ locked: external_exports.boolean().optional(),
9980
10041
  /**
9981
10042
  * Every internal endpoint this input fans out to (resolved form of
9982
10043
  * `TemplatePort.internalTargets`). Absent/empty = the single
@@ -10138,9 +10199,10 @@ var CropRectSchema = external_exports.object({
10138
10199
  });
10139
10200
  var StitchClipOrderSchema = external_exports.array(external_exports.string());
10140
10201
  var StitchScaleFitSchema = external_exports.enum(["fit", "stretch"]);
10202
+ var roundMs = (value) => typeof value === "number" ? Math.round(value) : value;
10141
10203
  var StitchTrimWindowFields = {
10142
- trimStartMs: external_exports.number().int().nonnegative().optional(),
10143
- trimEndMs: external_exports.number().int().positive().optional()
10204
+ trimStartMs: external_exports.preprocess(roundMs, external_exports.number().int().nonnegative()).optional(),
10205
+ trimEndMs: external_exports.preprocess(roundMs, external_exports.number().int().positive()).optional()
10144
10206
  };
10145
10207
  var StitchAudioOverlaySchema = external_exports.object({
10146
10208
  nodeId: external_exports.string(),
@@ -10156,6 +10218,8 @@ var AssetSummaryDetailsSchema = external_exports.object({
10156
10218
  summary: external_exports.string()
10157
10219
  });
10158
10220
  var AssetResponseSchema = external_exports.object({
10221
+ /** Omitted from persisted snapshots created before this field existed. */
10222
+ assetId: external_exports.string().uuid().optional(),
10159
10223
  url: external_exports.string().nullable(),
10160
10224
  originalUrl: external_exports.string().nullable(),
10161
10225
  previewUrl: external_exports.string().nullable(),
@@ -10302,6 +10366,8 @@ var NodeResponseSchema = external_exports.object({
10302
10366
  templatePorts: external_exports.array(InstancePortBindingSchema).nullable().optional(),
10303
10367
  /** The source template id on a template-instance group (from CRDT). */
10304
10368
  templateId: external_exports.string().uuid().nullable().optional(),
10369
+ /** Whether a template-instance group may be expanded to its workflow (from CRDT). */
10370
+ isExpandable: external_exports.boolean().nullable().optional(),
10305
10371
  /** Direct child node IDs when nodeType is group (for agent wiring). */
10306
10372
  childNodeIds: external_exports.array(external_exports.string().uuid()).optional(),
10307
10373
  /** Per-ratio outputs for magic-resize nodes (from CRDT); one per variant. */
@@ -10407,8 +10473,11 @@ var NodeRunStatusSchema = external_exports.enum([
10407
10473
  var CreateNodeRunResponseSchema = external_exports.object({
10408
10474
  id: external_exports.string().uuid()
10409
10475
  });
10476
+ var RunOriginSchema = external_exports.enum(["canvas", "playground"]);
10410
10477
  var RunNodeBodySchema = external_exports.object({
10411
10478
  canvasId: external_exports.string().uuid().optional(),
10479
+ /** Product surface this run originated on. Defaults to `canvas` server-side. */
10480
+ origin: RunOriginSchema.optional(),
10412
10481
  textPrompt: external_exports.string().optional(),
10413
10482
  modelConfig: ModelConfigInputSchema.optional(),
10414
10483
  outputAspectRatio: OutputAspectRatioSchema.optional(),
@@ -10723,7 +10792,13 @@ var CreateDirectNodeBodyObjectSchema = external_exports.object({
10723
10792
  /** For template-instance groups only: resolved input/output port bindings. */
10724
10793
  templatePorts: external_exports.array(InstancePortBindingSchema).nullable().optional(),
10725
10794
  /** For template-instance groups only: the id of the source template. */
10726
- templateId: external_exports.string().uuid().nullable().optional()
10795
+ templateId: external_exports.string().uuid().nullable().optional(),
10796
+ /** For template-instance groups only: whether the box may be expanded to its
10797
+ * workflow (snapshot of the template's setting at apply time). */
10798
+ isExpandable: external_exports.boolean().nullable().optional(),
10799
+ /** For template-instance groups only: node ids of the baked locked-input
10800
+ * sources, surfaced into the input column when the box is expanded. */
10801
+ lockedInputNodeIds: external_exports.array(external_exports.string().uuid()).nullable().optional()
10727
10802
  });
10728
10803
  var CreateDirectNodeBodySchema = CreateDirectNodeBodyObjectSchema.superRefine(
10729
10804
  validateDirectFileNodeAssetFields
@@ -11060,6 +11135,31 @@ var canvasContract = c7.router({
11060
11135
  404: MessageResponseSchema
11061
11136
  }
11062
11137
  },
11138
+ // Mint per-node presigned download URLs (Content-Disposition: attachment)
11139
+ // so a client can trigger a real file download. Used by the display_canvas
11140
+ // MCP App, whose sandboxed iframe cannot download directly and must hand a
11141
+ // download-forcing URL to the host. Returns only nodes with a downloadable
11142
+ // active-version asset; nodes without one are omitted.
11143
+ getNodeDownloadUrls: {
11144
+ method: "POST",
11145
+ path: "/canvases/:canvasId/node-download-urls",
11146
+ pathParams: external_exports.object({ canvasId: external_exports.string().uuid() }),
11147
+ body: external_exports.object({
11148
+ nodeIds: external_exports.array(external_exports.string().uuid()).min(1).max(100)
11149
+ }),
11150
+ responses: {
11151
+ 200: external_exports.object({
11152
+ urls: external_exports.array(
11153
+ external_exports.object({
11154
+ nodeId: external_exports.string().uuid(),
11155
+ downloadUrl: external_exports.string().url(),
11156
+ filename: external_exports.string()
11157
+ })
11158
+ )
11159
+ }),
11160
+ 404: MessageResponseSchema
11161
+ }
11162
+ },
11063
11163
  listCanvasAssets: {
11064
11164
  method: "GET",
11065
11165
  path: "/canvases/:canvasId/assets",
@@ -11451,6 +11551,8 @@ var canvasContract = c7.router({
11451
11551
  pathParams: external_exports.object({ canvasId: external_exports.string().uuid() }),
11452
11552
  body: external_exports.object({
11453
11553
  nodeIds: external_exports.array(external_exports.string().uuid()).min(1).max(500),
11554
+ /** Product surface this run originated on. Defaults to `canvas` server-side. */
11555
+ origin: RunOriginSchema.optional(),
11454
11556
  /** Explicit image URLs applied to every node in the bulk run (merged with edge-resolved images). */
11455
11557
  imageUrls: external_exports.array(external_exports.string().url()).optional(),
11456
11558
  /**
@@ -12135,6 +12237,10 @@ var ModelCostSchema = external_exports.discriminatedUnion("type", [
12135
12237
  maxCreditCost: external_exports.number()
12136
12238
  })
12137
12239
  ]);
12240
+ var TextTokenPricingSchema = external_exports.object({
12241
+ inputCreditsPerMillionTokens: external_exports.number(),
12242
+ outputCreditsPerMillionTokens: external_exports.number()
12243
+ });
12138
12244
  var InputFileSizeConstraintsResponseSchema = external_exports.object({
12139
12245
  maximumFileBytes: external_exports.number().int().nonnegative().optional(),
12140
12246
  maximumTotalBytes: external_exports.number().int().nonnegative().optional()
@@ -12240,7 +12346,9 @@ var ModelResponseSchema = external_exports.object({
12240
12346
  hiddenFromAgent: external_exports.boolean().optional(),
12241
12347
  promoRateLimit: PromoRateLimitSchema.optional(),
12242
12348
  /** Maximum prompt length in characters. Used by text models that lack variants. */
12243
- maxPromptLength: external_exports.number().nullable().optional()
12349
+ maxPromptLength: external_exports.number().nullable().optional(),
12350
+ /** Per-1M-token credit rates for text models; drives the live cost-range estimate. */
12351
+ textTokenPricing: TextTokenPricingSchema.optional()
12244
12352
  });
12245
12353
  var ListGenerativeModelsResponseSchema = external_exports.object({
12246
12354
  models: external_exports.array(ModelResponseSchema)
@@ -12733,7 +12841,8 @@ var EnterpriseOrderLineItemSchema = external_exports.object({
12733
12841
  var EnterpriseBillingIntervalSchema = external_exports.enum(["monthly", "annual"]);
12734
12842
  var EnterpriseStripeBillingInputSchema = external_exports.object({
12735
12843
  unitAmount: external_exports.number().int().positive(),
12736
- currency: external_exports.string().length(3).transform((value) => value.toLowerCase()),
12844
+ // Expose the transform output to OpenAPI generation.
12845
+ currency: external_exports.string().length(3).transform((value) => value.toLowerCase()).pipe(external_exports.string()),
12737
12846
  billingInterval: EnterpriseBillingIntervalSchema,
12738
12847
  creditsPerPeriod: external_exports.number().int().positive(),
12739
12848
  contractEndAt: external_exports.string().datetime().optional()
@@ -12797,7 +12906,8 @@ var EnterprisePendingExpansionSchema = external_exports.object({
12797
12906
  });
12798
12907
  var EnterpriseStripeBillingSchema = external_exports.object({
12799
12908
  unitAmount: external_exports.number().int().positive(),
12800
- currency: external_exports.string().length(3).transform((value) => value.toLowerCase()),
12909
+ // Expose the transform output to OpenAPI generation.
12910
+ currency: external_exports.string().length(3).transform((value) => value.toLowerCase()).pipe(external_exports.string()),
12801
12911
  billingInterval: EnterpriseBillingIntervalSchema,
12802
12912
  creditsPerPeriod: external_exports.number().int().positive(),
12803
12913
  contractEndAt: external_exports.string().datetime().optional(),
@@ -13511,10 +13621,13 @@ var publicContract = c16.router(
13511
13621
  complete: uploadContract.complete
13512
13622
  },
13513
13623
  asset: {
13514
- // Semantic/filename search of the team's Files (DAM) library, and
13515
- // placing a found asset onto a canvas by reference (no duplicate).
13516
- // Both are gated to Pro + Enterprise by the handlers.
13624
+ // Semantic/filename search of the team's Files (DAM) library,
13625
+ // listing the library's folders (to resolve a folder name id for a
13626
+ // folder-scoped search), and placing a found asset onto a canvas by
13627
+ // reference (no duplicate). All gated to Pro + Enterprise by the
13628
+ // handlers.
13517
13629
  search: filesContract.searchAgentAssets,
13630
+ folders: filesContract.listAgentAssetFolders,
13518
13631
  place: canvasContract.placeLibraryAssets
13519
13632
  },
13520
13633
  model: {
@@ -13781,7 +13894,7 @@ function isMajorUpgrade(current, latest) {
13781
13894
  return l[0] > c17[0];
13782
13895
  }
13783
13896
  function shouldShowUpgradeNotice(ctx = {}) {
13784
- const current = ctx.current ?? "0.5.2";
13897
+ const current = ctx.current ?? "0.7.0";
13785
13898
  const latest = ctx.latest ?? serverLatestVersion;
13786
13899
  const env = ctx.env ?? process.env;
13787
13900
  const isTty = ctx.isTty ?? Boolean(process.stderr.isTTY);
@@ -13802,7 +13915,7 @@ Upgrade: npm install -g @melius-ai/cli@latest (or: brew update && brew upgrade
13802
13915
  function printUpgradeNoticeIfNeeded(ctx = {}) {
13803
13916
  try {
13804
13917
  if (!shouldShowUpgradeNotice(ctx)) return;
13805
- const current = ctx.current ?? "0.5.2";
13918
+ const current = ctx.current ?? "0.7.0";
13806
13919
  const latest = ctx.latest ?? serverLatestVersion;
13807
13920
  process.stderr.write(formatUpgradeNotice(current, latest));
13808
13921
  } catch {
@@ -18486,37 +18599,65 @@ function registerAssetCommands(program2, getOutputOpts) {
18486
18599
  "Search and place the team's Files (DAM) library assets. Requires a Pro or Enterprise plan"
18487
18600
  );
18488
18601
  asset.command("search").description(
18489
- "Search the team's Files/DAM library (uploads + past generations) by meaning (semantic), with exact filename/title matches ranked first. Returns up to 5 relevant matches"
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"
18490
18603
  ).argument(
18491
- "<query>",
18492
- 'Search terms \u2014 a filename or a description of the content, e.g. "Product_Shot_05" or "black jacket shot"'
18493
- ).option("--limit <n>", "Max results (1-5, default 5)", "5").option(
18604
+ "[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'
18606
+ ).option("--limit <n>", "Max results per page (1-20, default 20)", "20").option(
18607
+ "--offset <n>",
18608
+ "Skip the first N matches to page deeper (default 0). Page with --limit until the running count reaches the response's total",
18609
+ "0"
18610
+ ).option(
18494
18611
  "--file-type <type>",
18495
18612
  "Filter by media type: image, video, audio, pdf, or text"
18496
18613
  ).option(
18497
18614
  "--canvas-id <canvasId>",
18498
18615
  "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
+ ).option(
18617
+ "--folder-id <folderId>",
18618
+ "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"
18499
18619
  ).addHelpText(
18500
18620
  "after",
18501
18621
  `
18502
18622
  Returns: { query, total, results: [{ assetId, filename, displayName, fileType, contentType, previewUrl, assetUrl, durationInMs }] }.
18623
+ total is the full match count; page through it with --limit + --offset.
18503
18624
  Use the returned assetId values with mel asset place to bring them onto a canvas.
18625
+ Resolve a folder name to an id with mel asset folders first.
18504
18626
 
18505
18627
  Examples:
18506
18628
  $ mel asset search "black jacket shot"
18507
- $ mel asset search "logo" --file-type image --limit 3`
18629
+ $ mel asset search "logo" --file-type image --limit 10
18630
+ $ mel asset search "logo" --limit 20 --offset 20
18631
+ $ mel asset search "shark" --folder-id <folderId> # find within a folder
18632
+ $ mel asset search --folder-id <folderId> # bring in the WHOLE folder`
18508
18633
  ).action(
18509
18634
  async (query, opts) => {
18635
+ if (query === void 0 && opts.folderId === void 0) {
18636
+ writeError(
18637
+ formatError(
18638
+ "USAGE_ERROR",
18639
+ "Provide a search query, or --folder-id to bring in a whole folder",
18640
+ {
18641
+ suggestion: "Run mel asset folders to list folder ids, then: mel asset search --folder-id <folderId>"
18642
+ }
18643
+ ),
18644
+ EXIT_USAGE_ERROR
18645
+ );
18646
+ }
18510
18647
  const client = getClient();
18511
18648
  const result = await client.asset.search({
18512
18649
  query: {
18513
- query,
18650
+ ...query !== void 0 && { query },
18514
18651
  limit: Number(opts.limit),
18652
+ offset: Number(opts.offset),
18515
18653
  ...opts.fileType !== void 0 && {
18516
18654
  fileType: opts.fileType
18517
18655
  },
18518
18656
  ...opts.canvasId !== void 0 && {
18519
18657
  canvasId: opts.canvasId
18658
+ },
18659
+ ...opts.folderId !== void 0 && {
18660
+ folderId: opts.folderId
18520
18661
  }
18521
18662
  }
18522
18663
  });
@@ -18530,6 +18671,31 @@ Examples:
18530
18671
  writeJson(result.body, getOutputOpts());
18531
18672
  }
18532
18673
  );
18674
+ asset.command("folders").description(
18675
+ "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"
18676
+ ).addHelpText(
18677
+ "after",
18678
+ `
18679
+ Returns: an array of { id, name, path, fileCount }.
18680
+ path is the folder's location from the library root (e.g. "Brand / Logos") \u2014 use it to disambiguate same-named folders.
18681
+ Pass a folder id to mel asset search --folder-id <folderId>.
18682
+
18683
+ Examples:
18684
+ $ mel asset folders
18685
+ $ mel asset folders --text`
18686
+ ).action(async () => {
18687
+ const client = getClient();
18688
+ const result = await client.asset.folders();
18689
+ if (result.status !== 200) {
18690
+ handleApiError(
18691
+ result.status,
18692
+ result.body,
18693
+ result.status === 403 ? ASSET_PLAN_REQUIRED_SUGGESTION : void 0
18694
+ );
18695
+ }
18696
+ const body = result.body;
18697
+ writeJson(body.folders, getOutputOpts());
18698
+ });
18533
18699
  asset.command("place").description(
18534
18700
  "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"
18535
18701
  ).argument(
@@ -18699,7 +18865,7 @@ function withDefaultHelp(args) {
18699
18865
  }
18700
18866
  function createProgram() {
18701
18867
  const program2 = new Command();
18702
- program2.name("mel").description("Mel \u2014 Melius agent CLI").version("0.5.2").showSuggestionAfterError(true).option("--json", "Force JSON output (default)").option("--text", "Human-readable output").option(
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(
18703
18869
  "--fields <fields>",
18704
18870
  "Select specific output fields (comma-separated)"
18705
18871
  ).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-4GUG5TD2.js";
5
+ } from "../chunk-5QT6WMID.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.5.2",
3
+ "version": "0.7.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",