@melius-ai/cli 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -255,6 +255,25 @@ mel upload ./photo.png
255
255
  mel upload ./photo.png --node-id <nodeId> --canvas-id <canvasId>
256
256
  ```
257
257
 
258
+ ### Assets (Files / DAM library)
259
+
260
+ Requires the Pro or Enterprise plan.
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) |
266
+
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
+
269
+ `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
+
271
+ ```bash
272
+ # Find an asset, then bring it onto a canvas and wire it
273
+ mel asset search "black jacket shot" --file-type image
274
+ mel asset place <canvasId> <assetId1> <assetId2>
275
+ ```
276
+
258
277
  ### Shell completion
259
278
 
260
279
  `mel completion <shell>` prints a tab-completion script for `bash`, `zsh`, or `fish`. The script is generated from the live command tree, so it never drifts from the actual commands.
package/dist/bin/mel.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  printUpgradeNoticeIfNeeded,
8
8
  withDefaultHelp,
9
9
  writeError
10
- } from "../chunk-EXHDKHNG.js";
10
+ } from "../chunk-SFL6BKJU.js";
11
11
 
12
12
  // bin/mel.ts
13
13
  var program = createProgram();
@@ -7757,9 +7757,6 @@ function paginationQuery({
7757
7757
  var c = initContract();
7758
7758
  var MAX_CUSTOM_CONTEXT_NAME_LENGTH = 80;
7759
7759
  var MAX_CUSTOM_CONTEXT_INSTRUCTIONS_LENGTH = 4e3;
7760
- var MAX_CUSTOM_CONTEXT_ASSET_DESCRIPTION_LENGTH = 500;
7761
- var MAX_CUSTOM_CONTEXT_FILE_SIZE_BYTES = 10 * 1024 * 1024;
7762
- var CUSTOM_CONTEXT_UPLOAD_PART_SIZE = 16 * 1024 * 1024;
7763
7760
  var MAX_AGENT_CUSTOM_CONTEXTS_PER_MESSAGE = 1;
7764
7761
  var CustomContextAssetSchema = external_exports.object({
7765
7762
  id: external_exports.string().uuid(),
@@ -7772,9 +7769,6 @@ var CustomContextAssetSchema = external_exports.object({
7772
7769
  previewText: external_exports.string().nullable(),
7773
7770
  description: external_exports.string().nullable()
7774
7771
  });
7775
- var UpdateCustomContextAssetBodySchema = external_exports.object({
7776
- description: external_exports.string().max(MAX_CUSTOM_CONTEXT_ASSET_DESCRIPTION_LENGTH).nullable()
7777
- });
7778
7772
  var CustomContextSummarySchema = external_exports.object({
7779
7773
  id: external_exports.string().uuid(),
7780
7774
  name: external_exports.string(),
@@ -7811,26 +7805,6 @@ var UpdateCustomContextBodySchema = external_exports.object({
7811
7805
  (body) => body.name !== void 0 || body.instructions !== void 0,
7812
7806
  { message: "At least one field must be provided" }
7813
7807
  );
7814
- var CustomContextAssetInitUploadSchema = external_exports.object({
7815
- filename: external_exports.string(),
7816
- contentType: external_exports.string(),
7817
- sizeInBytes: external_exports.number().int().positive().max(MAX_CUSTOM_CONTEXT_FILE_SIZE_BYTES)
7818
- });
7819
- var CustomContextAssetInitUploadResponseSchema = external_exports.object({
7820
- uploadId: external_exports.string(),
7821
- s3Bucket: external_exports.string(),
7822
- s3Key: external_exports.string(),
7823
- partSize: external_exports.number().int().positive()
7824
- });
7825
- var CustomContextAssetSignPartSchema = external_exports.object({
7826
- uploadId: external_exports.string(),
7827
- s3Bucket: external_exports.string(),
7828
- s3Key: external_exports.string(),
7829
- partNumber: external_exports.number().int().positive()
7830
- });
7831
- var CustomContextAssetSignPartResponseSchema = external_exports.object({
7832
- url: external_exports.string()
7833
- });
7834
7808
  var ImportCustomContextToCanvasResponseSchema = external_exports.object({
7835
7809
  groupId: external_exports.string().uuid(),
7836
7810
  nodeIds: external_exports.array(external_exports.string().uuid()),
@@ -7845,17 +7819,6 @@ var ImportCustomContextToCanvasResponseSchema = external_exports.object({
7845
7819
  })
7846
7820
  ).default([])
7847
7821
  });
7848
- var CustomContextAssetCompleteUploadSchema = external_exports.object({
7849
- uploadId: external_exports.string(),
7850
- s3Bucket: external_exports.string(),
7851
- s3Key: external_exports.string(),
7852
- filename: external_exports.string(),
7853
- contentType: external_exports.string(),
7854
- sizeInBytes: external_exports.number().int().positive().max(MAX_CUSTOM_CONTEXT_FILE_SIZE_BYTES),
7855
- previewText: external_exports.string().nullable().optional(),
7856
- description: external_exports.string().max(MAX_CUSTOM_CONTEXT_ASSET_DESCRIPTION_LENGTH).nullable().optional(),
7857
- parts: external_exports.array(CompletedPartSchema)
7858
- });
7859
7822
  var customContextContract = c.router({
7860
7823
  listCustomContexts: {
7861
7824
  method: "GET",
@@ -7920,80 +7883,6 @@ var customContextContract = c.router({
7920
7883
  404: MessageResponseSchema
7921
7884
  }
7922
7885
  },
7923
- initCustomContextAssetUpload: {
7924
- method: "POST",
7925
- path: "/teams/:teamId/custom-contexts/:contextId/assets/init",
7926
- pathParams: external_exports.object({
7927
- teamId: external_exports.string().uuid(),
7928
- contextId: external_exports.string().uuid()
7929
- }),
7930
- body: CustomContextAssetInitUploadSchema,
7931
- responses: {
7932
- 200: CustomContextAssetInitUploadResponseSchema,
7933
- 400: MessageResponseSchema,
7934
- 403: MessageResponseSchema,
7935
- 404: MessageResponseSchema
7936
- }
7937
- },
7938
- signCustomContextAssetUploadPart: {
7939
- method: "POST",
7940
- path: "/teams/:teamId/custom-contexts/:contextId/assets/sign-part",
7941
- pathParams: external_exports.object({
7942
- teamId: external_exports.string().uuid(),
7943
- contextId: external_exports.string().uuid()
7944
- }),
7945
- body: CustomContextAssetSignPartSchema,
7946
- responses: {
7947
- 200: CustomContextAssetSignPartResponseSchema,
7948
- 403: MessageResponseSchema,
7949
- 404: MessageResponseSchema
7950
- }
7951
- },
7952
- completeCustomContextAssetUpload: {
7953
- method: "POST",
7954
- path: "/teams/:teamId/custom-contexts/:contextId/assets/complete",
7955
- pathParams: external_exports.object({
7956
- teamId: external_exports.string().uuid(),
7957
- contextId: external_exports.string().uuid()
7958
- }),
7959
- body: CustomContextAssetCompleteUploadSchema,
7960
- responses: {
7961
- 200: CustomContextDetailSchema,
7962
- 400: MessageResponseSchema,
7963
- 403: MessageResponseSchema,
7964
- 404: MessageResponseSchema
7965
- }
7966
- },
7967
- removeCustomContextAsset: {
7968
- method: "DELETE",
7969
- path: "/teams/:teamId/custom-contexts/:contextId/assets/:assetId",
7970
- pathParams: external_exports.object({
7971
- teamId: external_exports.string().uuid(),
7972
- contextId: external_exports.string().uuid(),
7973
- assetId: external_exports.string().uuid()
7974
- }),
7975
- responses: {
7976
- 200: CustomContextDetailSchema,
7977
- 403: MessageResponseSchema,
7978
- 404: MessageResponseSchema
7979
- }
7980
- },
7981
- updateCustomContextAsset: {
7982
- method: "PATCH",
7983
- path: "/teams/:teamId/custom-contexts/:contextId/assets/:assetId",
7984
- pathParams: external_exports.object({
7985
- teamId: external_exports.string().uuid(),
7986
- contextId: external_exports.string().uuid(),
7987
- assetId: external_exports.string().uuid()
7988
- }),
7989
- body: UpdateCustomContextAssetBodySchema,
7990
- responses: {
7991
- 200: CustomContextDetailSchema,
7992
- 400: MessageResponseSchema,
7993
- 403: MessageResponseSchema,
7994
- 404: MessageResponseSchema
7995
- }
7996
- },
7997
7886
  importCustomContextToCanvas: {
7998
7887
  method: "POST",
7999
7888
  path: "/teams/:teamId/custom-contexts/:contextId/import-to-canvas/:canvasId",
@@ -8191,14 +8080,23 @@ var ListFilesResponseSchema = external_exports.object({
8191
8080
  hasMore: external_exports.boolean().optional()
8192
8081
  });
8193
8082
  var AgentAssetSearchQuerySchema = external_exports.object({
8194
- query: external_exports.string().trim().min(1).max(500).describe("Semantic asset search query"),
8083
+ query: external_exports.string().trim().min(1).max(500).describe("Asset search query"),
8195
8084
  limit: external_exports.coerce.number().int().min(1).max(5).default(5),
8196
8085
  source: FileSourceSchema.optional(),
8197
8086
  fileType: FileTypeSchema.optional(),
8198
8087
  canvasId: external_exports.string().uuid().optional().describe(
8199
8088
  '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
+ ),
8090
+ showInThread: external_exports.boolean().optional().describe(
8091
+ '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.'
8200
8092
  )
8201
8093
  });
8094
+ var AgentAssetSearchModeSchema = external_exports.enum(["semantic", "filename"]);
8095
+ var AgentAssetSearchRequestSchema = AgentAssetSearchQuerySchema.extend(
8096
+ {
8097
+ mode: AgentAssetSearchModeSchema.optional()
8098
+ }
8099
+ );
8202
8100
  var AgentAssetSearchResponseSchema = external_exports.object({
8203
8101
  query: external_exports.string(),
8204
8102
  results: external_exports.array(FileCardSchema),
@@ -8373,7 +8271,7 @@ var filesContract = c2.router({
8373
8271
  searchAgentAssets: {
8374
8272
  method: "GET",
8375
8273
  path: "/files/agent-search",
8376
- query: AgentAssetSearchQuerySchema,
8274
+ query: AgentAssetSearchRequestSchema,
8377
8275
  responses: {
8378
8276
  200: AgentAssetSearchResponseSchema,
8379
8277
  403: ErrorBodySchema
@@ -9135,11 +9033,10 @@ var AUTO_MODEL_REGISTRY = [
9135
9033
  {
9136
9034
  nodeType: "video",
9137
9035
  inputPattern: "frames-with-audio",
9138
- // Start + end frame + audio falls back to ltx-2.3-audio because
9139
- // kling-avatar's variant does not accept end_image_url, so
9140
- // variant selection would reject the second image.
9141
- standard: { model: "ltx-2.3-audio", variant: "image-to-video" },
9142
- fast: { model: "ltx-2.3-audio", variant: "image-to-video" }
9036
+ // Kling Avatar does not accept end_image_url, so route frame+audio
9037
+ // runs through LTX's audio-capable image-to-video variant.
9038
+ standard: { model: "ltx-2.3", variant: "image-to-video" },
9039
+ fast: { model: "ltx-2.3", variant: "image-to-video" }
9143
9040
  },
9144
9041
  {
9145
9042
  nodeType: "video",
@@ -9230,7 +9127,11 @@ var AutoModelProxy = class {
9230
9127
  resolve(input) {
9231
9128
  switch (input.nodeType) {
9232
9129
  case "video":
9233
- return this.resolveVideo(input.connectedInputs, input.tier);
9130
+ return this.resolveVideo(
9131
+ input.connectedInputs,
9132
+ input.tier,
9133
+ input.canvasVideoInputRoutingEnabled === true
9134
+ );
9234
9135
  case "image":
9235
9136
  return this.resolveImage(input.connectedInputs, input.tier);
9236
9137
  case "text":
@@ -9249,7 +9150,7 @@ var AutoModelProxy = class {
9249
9150
  return this.registry;
9250
9151
  }
9251
9152
  // ── Private resolvers ───────────────────────────────────────────────
9252
- resolveVideo(ci, tier) {
9153
+ resolveVideo(ci, tier, canvasVideoInputRoutingEnabled) {
9253
9154
  const hasAnyImage = ci.hasImage || ci.hasStartingImage || ci.hasEndImage || ci.hasReferenceImage;
9254
9155
  if (ci.hasVideo && hasAnyImage) {
9255
9156
  return {
@@ -9273,7 +9174,11 @@ var AutoModelProxy = class {
9273
9174
  return this.lookupRoute("video", "video-with-audio", tier);
9274
9175
  }
9275
9176
  if (ci.hasAudio && (ci.hasStartingImage || ci.hasImage) && ci.hasEndImage) {
9276
- return this.lookupRoute("video", "frames-with-audio", tier);
9177
+ return canvasVideoInputRoutingEnabled ? this.lookupRoute("video", "frames-with-audio", tier) : {
9178
+ valid: true,
9179
+ model: "ltx-2.3-audio",
9180
+ variant: "image-to-video"
9181
+ };
9277
9182
  }
9278
9183
  if (ci.hasAudio && hasAnyImage) {
9279
9184
  return this.lookupRoute("video", "image-with-audio", tier);
@@ -9423,6 +9328,7 @@ var GENERATION_MODES = [
9423
9328
  "video-to-video",
9424
9329
  "reference-to-video",
9425
9330
  "first-last-frame-to-video",
9331
+ "audio-to-video",
9426
9332
  "text-to-speech",
9427
9333
  "audio-to-audio",
9428
9334
  "text-to-sfx",
@@ -9811,6 +9717,7 @@ var ElevenLabsVoiceSettingsSchema = external_exports.object({
9811
9717
 
9812
9718
  // ../api-contract/src/lora.ts
9813
9719
  var c6 = initContract();
9720
+ var LORA_UPLOAD_PART_SIZE_BYTES = 16 * 1024 * 1024;
9814
9721
  var MAX_LORA_NAME_LENGTH = 80;
9815
9722
  var MAX_LORA_DESCRIPTION_LENGTH = 1e3;
9816
9723
  var MAX_LORA_TRIGGER_WORDS_LENGTH = 500;
@@ -9819,10 +9726,10 @@ var MAX_LORAS_PER_GENERATION = 5;
9819
9726
  var MIN_LORA_WEIGHT = 0;
9820
9727
  var MAX_LORA_WEIGHT = 1.5;
9821
9728
  var MIN_SDXL_STEPS = 1;
9822
- var MAX_SDXL_STEPS = 60;
9729
+ var MAX_SDXL_STEPS = 50;
9823
9730
  var MIN_SDXL_GUIDANCE_SCALE = 0;
9824
9731
  var MAX_SDXL_GUIDANCE_SCALE = 20;
9825
- var LoraBaseModelSchema = external_exports.enum(["sdxl", "flux", "other"]);
9732
+ var LoraBaseModelSchema = external_exports.enum(["sdxl", "flux", "flux-2", "other"]);
9826
9733
  var LoraSelectionSchema = external_exports.object({
9827
9734
  id: external_exports.string().uuid(),
9828
9735
  weight: external_exports.number().min(MIN_LORA_WEIGHT).max(MAX_LORA_WEIGHT)
@@ -9859,10 +9766,22 @@ var InitLoraUploadBodySchema = external_exports.object({
9859
9766
  sizeInBytes: external_exports.number().int().positive().max(MAX_LORA_FILE_SIZE_BYTES)
9860
9767
  });
9861
9768
  var InitLoraUploadResponseSchema = external_exports.object({
9862
- /** Short-lived presigned PUT the client uploads the raw weights to. */
9863
- uploadUrl: external_exports.string(),
9769
+ /** S3 multipart upload id the client threads into sign-part + create. */
9770
+ uploadId: external_exports.string(),
9864
9771
  s3Bucket: external_exports.string(),
9865
- s3Key: external_exports.string()
9772
+ s3Key: external_exports.string(),
9773
+ /** Byte size of each part except the last (see LORA_UPLOAD_PART_SIZE_BYTES). */
9774
+ partSize: external_exports.number().int().positive()
9775
+ });
9776
+ var SignLoraUploadPartBodySchema = external_exports.object({
9777
+ uploadId: external_exports.string().min(1),
9778
+ s3Bucket: external_exports.string().min(1),
9779
+ s3Key: external_exports.string().min(1),
9780
+ partNumber: external_exports.number().int().positive()
9781
+ });
9782
+ var SignLoraUploadPartResponseSchema = external_exports.object({
9783
+ /** Short-lived presigned PUT for this one part. */
9784
+ url: external_exports.string()
9866
9785
  });
9867
9786
  var CreateLoraBodySchema = external_exports.object({
9868
9787
  name: external_exports.string().trim().min(1).max(MAX_LORA_NAME_LENGTH),
@@ -9872,11 +9791,23 @@ var CreateLoraBodySchema = external_exports.object({
9872
9791
  baseModel: LoraBaseModelSchema,
9873
9792
  s3Bucket: external_exports.string().min(1),
9874
9793
  s3Key: external_exports.string().min(1),
9875
- /** Client-attested SHA-256 of the uploaded file (integrity reference). */
9876
- sha256: external_exports.string().min(1),
9794
+ /** Multipart upload id + parts to finalize before validating the object. */
9795
+ uploadId: external_exports.string().min(1),
9796
+ parts: external_exports.array(CompletedPartSchema).min(1),
9797
+ /**
9798
+ * Optional client-attested SHA-256 (integrity reference, never verified).
9799
+ * Omitted for large files — hashing gigabytes in the browser OOMs the tab —
9800
+ * where S3 multipart part ETags already guarantee integrity.
9801
+ */
9802
+ sha256: external_exports.string().optional(),
9877
9803
  fileSizeBytes: external_exports.number().int().positive().max(MAX_LORA_FILE_SIZE_BYTES),
9878
9804
  defaultScale: external_exports.number().min(MIN_LORA_WEIGHT).max(MAX_LORA_WEIGHT).optional()
9879
9805
  });
9806
+ var UpdateLoraBodySchema = external_exports.object({
9807
+ name: external_exports.string().trim().min(1).max(MAX_LORA_NAME_LENGTH).optional(),
9808
+ description: external_exports.string().max(MAX_LORA_DESCRIPTION_LENGTH).optional(),
9809
+ triggerWords: external_exports.string().max(MAX_LORA_TRIGGER_WORDS_LENGTH).optional()
9810
+ });
9880
9811
  var ForbiddenSchema = MessageResponseSchema;
9881
9812
  var NotFoundSchema = MessageResponseSchema;
9882
9813
  var BadRequestSchema = MessageResponseSchema;
@@ -9892,7 +9823,7 @@ var loraContract = c6.router({
9892
9823
  404: NotFoundSchema
9893
9824
  }
9894
9825
  },
9895
- // @SkipManagedTransaction on the handler — only mints a presigned PUT.
9826
+ // @SkipManagedTransaction on the handler — only starts an S3 multipart.
9896
9827
  initLoraUpload: {
9897
9828
  method: "POST",
9898
9829
  path: "/teams/:teamId/loras/upload",
@@ -9904,6 +9835,18 @@ var loraContract = c6.router({
9904
9835
  403: ForbiddenSchema
9905
9836
  }
9906
9837
  },
9838
+ // @SkipManagedTransaction on the handler — only mints a presigned part PUT.
9839
+ signLoraUploadPart: {
9840
+ method: "POST",
9841
+ path: "/teams/:teamId/loras/upload/sign-part",
9842
+ pathParams: external_exports.object({ teamId: external_exports.string().uuid() }),
9843
+ body: SignLoraUploadPartBodySchema,
9844
+ responses: {
9845
+ 200: SignLoraUploadPartResponseSchema,
9846
+ 400: BadRequestSchema,
9847
+ 403: ForbiddenSchema
9848
+ }
9849
+ },
9907
9850
  createLora: {
9908
9851
  method: "POST",
9909
9852
  path: "/teams/:teamId/loras",
@@ -9916,6 +9859,22 @@ var loraContract = c6.router({
9916
9859
  409: ConflictSchema
9917
9860
  }
9918
9861
  },
9862
+ updateLora: {
9863
+ method: "PATCH",
9864
+ path: "/teams/:teamId/loras/:loraId",
9865
+ pathParams: external_exports.object({
9866
+ teamId: external_exports.string().uuid(),
9867
+ loraId: external_exports.string().uuid()
9868
+ }),
9869
+ body: UpdateLoraBodySchema,
9870
+ responses: {
9871
+ 200: LoraSummarySchema,
9872
+ 400: BadRequestSchema,
9873
+ 403: ForbiddenSchema,
9874
+ 404: NotFoundSchema,
9875
+ 409: ConflictSchema
9876
+ }
9877
+ },
9919
9878
  deleteLora: {
9920
9879
  method: "DELETE",
9921
9880
  path: "/teams/:teamId/loras/:loraId",
@@ -9968,6 +9927,16 @@ var TemplatePortSchema = external_exports.discriminatedUnion("direction", [
9968
9927
  required: external_exports.boolean().optional(),
9969
9928
  /** Creator-set cap on connections into this input. Absent = unlimited. */
9970
9929
  maxConnections: external_exports.number().int().positive().optional(),
9930
+ /**
9931
+ * Hidden input: not exposed as a connectable handle on the collapsed box
9932
+ * — its value comes from the source baked into the template definition
9933
+ * (used as-is for an upload/custom text, regenerated for a generation
9934
+ * node). The port is still recorded (rather than dropped) so it
9935
+ * round-trips for editing in the internal tool. At apply, hidden input
9936
+ * ports are not materialized as instance bindings, so no handle renders
9937
+ * and they never gate a run.
9938
+ */
9939
+ hidden: external_exports.boolean().optional(),
9971
9940
  /**
9972
9941
  * Every internal handle this input fans out to. Absent/empty = the
9973
9942
  * single `nodeKey`/`internalHandle` binding (pre-fan-out templates).
@@ -10001,6 +9970,13 @@ var InstancePortBindingSchema = external_exports.discriminatedUnion("direction",
10001
9970
  direction: external_exports.literal("input"),
10002
9971
  required: external_exports.boolean().optional(),
10003
9972
  maxConnections: external_exports.number().int().positive().optional(),
9973
+ /**
9974
+ * Mirror of `TemplatePort.hidden`. Hidden input ports are not
9975
+ * materialized as instance bindings today (the box never renders them),
9976
+ * so this is carried for symmetry/forward-compat rather than read at
9977
+ * runtime.
9978
+ */
9979
+ hidden: external_exports.boolean().optional(),
10004
9980
  /**
10005
9981
  * Every internal endpoint this input fans out to (resolved form of
10006
9982
  * `TemplatePort.internalTargets`). Absent/empty = the single
@@ -11813,6 +11789,35 @@ var canvasContract = c7.router({
11813
11789
  404: MessageResponseSchema
11814
11790
  }
11815
11791
  },
11792
+ // Server-side placement of existing library assets onto a canvas, by id.
11793
+ // Unlike `linkLibraryAsset` (which links an asset to a node the in-app
11794
+ // client already created in the CRDT), this CREATES the file node(s)
11795
+ // server-side and references the ORIGINAL asset (no duplicate) — the path
11796
+ // MCP/CLI need, since they have no browser canvas store. Positions are
11797
+ // planned server-side into open space. Gated by the same asset-search
11798
+ // entitlement (`isAgentSearchToolAvailable`) as `searchAgentAssets`.
11799
+ placeLibraryAssets: {
11800
+ method: "POST",
11801
+ path: "/canvases/:canvasId/library-assets/place",
11802
+ pathParams: external_exports.object({ canvasId: external_exports.string().uuid() }),
11803
+ body: external_exports.object({
11804
+ assetIds: external_exports.array(external_exports.string().uuid()).min(1).max(10)
11805
+ }),
11806
+ responses: {
11807
+ 200: external_exports.object({
11808
+ placed: external_exports.array(
11809
+ external_exports.object({
11810
+ assetId: external_exports.string().uuid(),
11811
+ nodeId: external_exports.string().uuid(),
11812
+ mediaType: external_exports.enum(["image", "video", "audio", "pdf"])
11813
+ })
11814
+ )
11815
+ }),
11816
+ 403: external_exports.object({ message: external_exports.string() }),
11817
+ 404: external_exports.object({ message: external_exports.string() })
11818
+ },
11819
+ summary: "Place library assets onto a canvas"
11820
+ },
11816
11821
  // ── Canvas Settings ──────────────────────────────────────────────
11817
11822
  getCanvasSettings: {
11818
11823
  method: "GET",
@@ -11911,10 +11916,16 @@ var CompleteCustomFontUploadBodySchema = external_exports.object({
11911
11916
  sizeInBytes: external_exports.number().int().positive(),
11912
11917
  parts: external_exports.array(CompletedPartSchema)
11913
11918
  });
11919
+ var ListCustomFontsQuerySchema = external_exports.object({
11920
+ // Optional name filter (matches family name or original filename). Used by
11921
+ // the Assets page fonts view; the node font pickers omit it to list all.
11922
+ search: external_exports.string().trim().optional()
11923
+ });
11914
11924
  var customFontContract = c8.router({
11915
11925
  list: {
11916
11926
  method: "GET",
11917
11927
  path: "/custom-fonts",
11928
+ query: ListCustomFontsQuerySchema,
11918
11929
  responses: { 200: ListCustomFontsResponseSchema },
11919
11930
  summary: "List custom fonts for the team"
11920
11931
  },
@@ -12113,6 +12124,15 @@ var ModelCostSchema = external_exports.discriminatedUnion("type", [
12113
12124
  external_exports.object({
12114
12125
  type: external_exports.literal("estimated"),
12115
12126
  creditCost: external_exports.number()
12127
+ }),
12128
+ // A cost that varies between a floor and a ceiling depending on inputs — e.g.
12129
+ // text models that accept video input are free for text-to-text
12130
+ // (`minCreditCost`) but charge a flat fee when a video is attached
12131
+ // (`maxCreditCost`). Rendered as a "min–max" range.
12132
+ external_exports.object({
12133
+ type: external_exports.literal("range"),
12134
+ minCreditCost: external_exports.number(),
12135
+ maxCreditCost: external_exports.number()
12116
12136
  })
12117
12137
  ]);
12118
12138
  var InputFileSizeConstraintsResponseSchema = external_exports.object({
@@ -12166,6 +12186,14 @@ var ModelVariantResponseSchema = external_exports.object({
12166
12186
  maxReferenceImagesWithEndImage: external_exports.number().nullable().optional(),
12167
12187
  maxVideoInputs: external_exports.number().nullable().optional(),
12168
12188
  maxAudioInputs: external_exports.number().nullable().optional(),
12189
+ /**
12190
+ * For variants accepting both video and audio: whether the connected
12191
+ * durations must line up. `false` for lipsync models (e.g. Kling Lipsync)
12192
+ * whose endpoint takes audio independent of the video length, so the
12193
+ * frontend preflight must not block a mismatch. Omitted/null means the
12194
+ * default duration-match requirement applies.
12195
+ */
12196
+ requiresMatchingAudioVideoDuration: external_exports.boolean().nullable().optional(),
12169
12197
  maxNativeOutputs: external_exports.number().nullable(),
12170
12198
  maxInputArea: external_exports.number().nullable(),
12171
12199
  inputImageFileSizeConstraints: InputFileSizeConstraintsResponseSchema.nullable().optional(),
@@ -12188,7 +12216,13 @@ var ModelVariantResponseSchema = external_exports.object({
12188
12216
  estimatedLatencySeconds: external_exports.number().nullable().optional(),
12189
12217
  defaultResolution: OutputResolutionSchema.nullable().optional(),
12190
12218
  featuredDescription: external_exports.string().nullable(),
12191
- cost: ModelCostSchema.optional()
12219
+ cost: ModelCostSchema.optional(),
12220
+ /**
12221
+ * Per-second credit multiplier applied to connected input video duration,
12222
+ * using the resolution-adjusted output per-second price as the base. Lets
12223
+ * the cost preview include the input-video surcharge that the server bills.
12224
+ */
12225
+ inputVideoCreditMultiplier: external_exports.number().optional()
12192
12226
  });
12193
12227
  var ModelResponseSchema = external_exports.object({
12194
12228
  name: external_exports.string(),
@@ -12710,6 +12744,34 @@ var EnterpriseStripeBillingInputSchema = external_exports.object({
12710
12744
  path: ["contractEndAt"]
12711
12745
  }
12712
12746
  );
12747
+ var CustomPlanCreditRolloverPolicySchema = external_exports.discriminatedUnion(
12748
+ "mode",
12749
+ [
12750
+ external_exports.object({ mode: external_exports.literal("none") }),
12751
+ external_exports.object({ mode: external_exports.literal("unlimited") }),
12752
+ external_exports.object({
12753
+ mode: external_exports.literal("months"),
12754
+ months: external_exports.number().int().nonnegative()
12755
+ }),
12756
+ external_exports.object({
12757
+ mode: external_exports.literal("percentage"),
12758
+ percentage: external_exports.number().int().min(0).max(100)
12759
+ })
12760
+ ]
12761
+ );
12762
+ var EnterpriseOrderDetailsSchema = external_exports.object({
12763
+ orderType: external_exports.string().min(1),
12764
+ customPlanType: CustomPlanTypeSchema.optional(),
12765
+ creditPackageAmount: external_exports.number().int().nonnegative(),
12766
+ creditPackageUnit: external_exports.string().min(1),
12767
+ creditRolloverPolicy: CustomPlanCreditRolloverPolicySchema.optional(),
12768
+ orderDate: external_exports.string().date(),
12769
+ /** Omit for plans with no fixed contract term (e.g. monthly Stripe plans). */
12770
+ termCount: external_exports.number().int().positive().optional(),
12771
+ termUnit: external_exports.string().min(1).optional(),
12772
+ lineItems: external_exports.array(EnterpriseOrderLineItemSchema),
12773
+ totalContractValue: external_exports.string().min(1)
12774
+ });
12713
12775
  var EnterprisePendingExpansionSchema = external_exports.object({
12714
12776
  /** Stable id minted at prepare; also the credit-grant idempotency key. */
12715
12777
  id: external_exports.string(),
@@ -12723,6 +12785,13 @@ var EnterprisePendingExpansionSchema = external_exports.object({
12723
12785
  /** New contract end when the operator chose a new term; absent means keep the existing term. */
12724
12786
  contractEndAt: external_exports.string().datetime().optional(),
12725
12787
  keepContractEnd: external_exports.boolean(),
12788
+ /**
12789
+ * Operator-authored replacement order details (line items, total contract
12790
+ * value, credit package, term) applied to the stored order when the customer
12791
+ * activates. Absent keeps the existing terms. Purely presentational — the
12792
+ * actual charge/credits come from the billing figures above.
12793
+ */
12794
+ newOrderDetails: EnterpriseOrderDetailsSchema.optional(),
12726
12795
  preparedAt: external_exports.string().datetime(),
12727
12796
  preparedByEmail: external_exports.string().nullable()
12728
12797
  });
@@ -12758,33 +12827,7 @@ var EnterpriseStripeBillingAuditEntrySchema = external_exports.object({
12758
12827
  previousStripePriceId: external_exports.string().optional(),
12759
12828
  notes: external_exports.string().optional()
12760
12829
  });
12761
- var CustomPlanCreditRolloverPolicySchema = external_exports.discriminatedUnion(
12762
- "mode",
12763
- [
12764
- external_exports.object({ mode: external_exports.literal("none") }),
12765
- external_exports.object({ mode: external_exports.literal("unlimited") }),
12766
- external_exports.object({
12767
- mode: external_exports.literal("months"),
12768
- months: external_exports.number().int().nonnegative()
12769
- }),
12770
- external_exports.object({
12771
- mode: external_exports.literal("percentage"),
12772
- percentage: external_exports.number().int().min(0).max(100)
12773
- })
12774
- ]
12775
- );
12776
- var EnterpriseOrderSchema = external_exports.object({
12777
- orderType: external_exports.string().min(1),
12778
- customPlanType: CustomPlanTypeSchema.optional(),
12779
- creditPackageAmount: external_exports.number().int().nonnegative(),
12780
- creditPackageUnit: external_exports.string().min(1),
12781
- creditRolloverPolicy: CustomPlanCreditRolloverPolicySchema.optional(),
12782
- orderDate: external_exports.string().date(),
12783
- /** Omit for plans with no fixed contract term (e.g. monthly Stripe plans). */
12784
- termCount: external_exports.number().int().positive().optional(),
12785
- termUnit: external_exports.string().min(1).optional(),
12786
- lineItems: external_exports.array(EnterpriseOrderLineItemSchema),
12787
- totalContractValue: external_exports.string().min(1),
12830
+ var EnterpriseOrderSchema = EnterpriseOrderDetailsSchema.extend({
12788
12831
  stripeBilling: EnterpriseStripeBillingInputSchema.optional()
12789
12832
  });
12790
12833
  var CustomerEnterpriseOrderSchema = EnterpriseOrderSchema.extend({
@@ -13467,6 +13510,13 @@ var publicContract = c16.router(
13467
13510
  signPart: uploadContract.signPart,
13468
13511
  complete: uploadContract.complete
13469
13512
  },
13513
+ 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.
13517
+ search: filesContract.searchAgentAssets,
13518
+ place: canvasContract.placeLibraryAssets
13519
+ },
13470
13520
  model: {
13471
13521
  list: generationContract.listGenerativeModels,
13472
13522
  autoDefaults: generationContract.getAutoModelDefaults
@@ -13731,7 +13781,7 @@ function isMajorUpgrade(current, latest) {
13731
13781
  return l[0] > c17[0];
13732
13782
  }
13733
13783
  function shouldShowUpgradeNotice(ctx = {}) {
13734
- const current = ctx.current ?? "0.4.0";
13784
+ const current = ctx.current ?? "0.5.1";
13735
13785
  const latest = ctx.latest ?? serverLatestVersion;
13736
13786
  const env = ctx.env ?? process.env;
13737
13787
  const isTty = ctx.isTty ?? Boolean(process.stderr.isTTY);
@@ -13746,13 +13796,13 @@ function formatUpgradeNotice(current, latest) {
13746
13796
  const breaking = isMajorUpgrade(current, latest) ? " This is a new major version that may include breaking changes." : "";
13747
13797
  return `
13748
13798
  mel ${latest} is available (you have ${current}).${breaking}
13749
- Upgrade: npm install -g @melius-ai/cli@latest (or: brew upgrade mel) Silence: MEL_NO_UPDATE_NOTIFIER=1
13799
+ Upgrade: npm install -g @melius-ai/cli@latest (or: brew update && brew upgrade mel) Silence: MEL_NO_UPDATE_NOTIFIER=1
13750
13800
  `;
13751
13801
  }
13752
13802
  function printUpgradeNoticeIfNeeded(ctx = {}) {
13753
13803
  try {
13754
13804
  if (!shouldShowUpgradeNotice(ctx)) return;
13755
- const current = ctx.current ?? "0.4.0";
13805
+ const current = ctx.current ?? "0.5.1";
13756
13806
  const latest = ctx.latest ?? serverLatestVersion;
13757
13807
  process.stderr.write(formatUpgradeNotice(current, latest));
13758
13808
  } catch {
@@ -18429,6 +18479,82 @@ Examples:
18429
18479
  );
18430
18480
  }
18431
18481
 
18482
+ // src/commands/asset.ts
18483
+ function registerAssetCommands(program2, getOutputOpts) {
18484
+ const asset = program2.command("asset").description(
18485
+ "Search and place the team's Files (DAM) library assets. Requires a Pro or Enterprise plan"
18486
+ );
18487
+ asset.command("search").description(
18488
+ "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"
18489
+ ).argument(
18490
+ "<query>",
18491
+ 'Search terms \u2014 a filename or a description of the content, e.g. "Product_Shot_05" or "black jacket shot"'
18492
+ ).option("--limit <n>", "Max results (1-5, default 5)", "5").option(
18493
+ "--file-type <type>",
18494
+ "Filter by media type: image, video, audio, pdf, or text"
18495
+ ).option(
18496
+ "--canvas-id <canvasId>",
18497
+ "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)"
18498
+ ).addHelpText(
18499
+ "after",
18500
+ `
18501
+ Returns: { query, total, results: [{ assetId, filename, displayName, fileType, contentType, previewUrl, assetUrl, durationInMs }] }.
18502
+ Use the returned assetId values with mel asset place to bring them onto a canvas.
18503
+
18504
+ Examples:
18505
+ $ mel asset search "black jacket shot"
18506
+ $ mel asset search "logo" --file-type image --limit 3`
18507
+ ).action(
18508
+ async (query, opts) => {
18509
+ const client = getClient();
18510
+ const result = await client.asset.search({
18511
+ query: {
18512
+ query,
18513
+ limit: Number(opts.limit),
18514
+ ...opts.fileType !== void 0 && {
18515
+ fileType: opts.fileType
18516
+ },
18517
+ ...opts.canvasId !== void 0 && {
18518
+ canvasId: opts.canvasId
18519
+ }
18520
+ }
18521
+ });
18522
+ if (result.status !== 200) {
18523
+ handleApiError(result.status, result.body);
18524
+ }
18525
+ writeJson(result.body, getOutputOpts());
18526
+ }
18527
+ );
18528
+ asset.command("place").description(
18529
+ "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"
18530
+ ).argument(
18531
+ "<canvasId>",
18532
+ "Canvas ID to place onto (from mel canvas create or mel canvas list)"
18533
+ ).argument(
18534
+ "<assetIds...>",
18535
+ "One or more asset IDs from mel asset search (max 10)"
18536
+ ).addHelpText(
18537
+ "after",
18538
+ `
18539
+ Returns: { placed: [{ assetId, nodeId, mediaType }] }.
18540
+ Wire the returned nodeIds into generations with mel edge create.
18541
+
18542
+ Examples:
18543
+ $ mel asset place <canvasId> <assetId>
18544
+ $ mel asset place <canvasId> <assetId1> <assetId2>`
18545
+ ).action(async (canvasId, assetIds) => {
18546
+ const client = getClient();
18547
+ const result = await client.asset.place({
18548
+ params: { canvasId },
18549
+ body: { assetIds }
18550
+ });
18551
+ if (result.status !== 200) {
18552
+ handleApiError(result.status, result.body);
18553
+ }
18554
+ writeJson(result.body, getOutputOpts());
18555
+ });
18556
+ }
18557
+
18432
18558
  // src/commands/completion.ts
18433
18559
  var GLOBAL_FLAGS = [
18434
18560
  "--json",
@@ -18564,7 +18690,7 @@ function withDefaultHelp(args) {
18564
18690
  }
18565
18691
  function createProgram() {
18566
18692
  const program2 = new Command();
18567
- program2.name("mel").description("Mel \u2014 Melius agent CLI").version("0.4.0").showSuggestionAfterError(true).option("--json", "Force JSON output (default)").option("--text", "Human-readable output").option(
18693
+ program2.name("mel").description("Mel \u2014 Melius agent CLI").version("0.5.1").showSuggestionAfterError(true).option("--json", "Force JSON output (default)").option("--text", "Human-readable output").option(
18568
18694
  "--fields <fields>",
18569
18695
  "Select specific output fields (comma-separated)"
18570
18696
  ).option("--quiet", "Suppress output, exit code only").option("--verbose", "Log request/response metadata to stderr").addHelpText(
@@ -18608,6 +18734,7 @@ Learn more: https://docs.melius.com`
18608
18734
  registerSkillCommands(program2, getOutputOpts);
18609
18735
  }
18610
18736
  registerUploadCommands(program2, getOutputOpts);
18737
+ registerAssetCommands(program2, getOutputOpts);
18611
18738
  registerCompletionCommands(program2);
18612
18739
  applyStructuredErrors(program2);
18613
18740
  return program2;
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-EXHDKHNG.js";
5
+ } from "../chunk-SFL6BKJU.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.4.0",
3
+ "version": "0.5.1",
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",