@melius-ai/cli 0.3.0 → 0.5.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
@@ -25,6 +25,20 @@ mel run wait <runId>
25
25
  2. In the Melius app, go to **Team settings → Integrations** and create an API key (it starts with `mel_` and is shown once — copy it immediately).
26
26
  3. Authenticate the CLI with it: `mel auth login --api-key mel_<your-key>`.
27
27
 
28
+ ## Upgrading
29
+
30
+ Check your installed version with `mel --version`. Upgrade with the same tool you installed with:
31
+
32
+ ```bash
33
+ # npm
34
+ npm install -g @melius-ai/cli@latest
35
+
36
+ # Homebrew (macOS/Linux)
37
+ brew update && brew upgrade mel
38
+ ```
39
+
40
+ To see the latest published version, run `npm view @melius-ai/cli version` (or visit the [package page on npm](https://www.npmjs.com/package/@melius-ai/cli)). Releases follow [semantic versioning](https://semver.org) — a major version bump (for example `1.x` → `2.x`) can include breaking changes, so review what changed before upgrading across one.
41
+
28
42
  ## Sandbox setup
29
43
 
30
44
  In the Claude Code sandbox, only two environment variables are needed:
@@ -241,6 +255,25 @@ mel upload ./photo.png
241
255
  mel upload ./photo.png --node-id <nodeId> --canvas-id <canvasId>
242
256
  ```
243
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
+
244
277
  ### Shell completion
245
278
 
246
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
@@ -4,9 +4,10 @@ import {
4
4
  EXIT_API_ERROR,
5
5
  createProgram,
6
6
  formatError,
7
+ printUpgradeNoticeIfNeeded,
7
8
  withDefaultHelp,
8
9
  writeError
9
- } from "../chunk-JVD4O5T3.js";
10
+ } from "../chunk-UAK5HMFU.js";
10
11
 
11
12
  // bin/mel.ts
12
13
  var program = createProgram();
@@ -23,3 +24,6 @@ try {
23
24
  EXIT_API_ERROR
24
25
  );
25
26
  }
27
+ printUpgradeNoticeIfNeeded({
28
+ quiet: Boolean(program.opts().quiet)
29
+ });
@@ -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,7 +8080,7 @@ 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(),
@@ -8199,6 +8088,12 @@ var AgentAssetSearchQuerySchema = external_exports.object({
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.'
8200
8089
  )
8201
8090
  });
8091
+ var AgentAssetSearchModeSchema = external_exports.enum(["semantic", "filename"]);
8092
+ var AgentAssetSearchRequestSchema = AgentAssetSearchQuerySchema.extend(
8093
+ {
8094
+ mode: AgentAssetSearchModeSchema.optional()
8095
+ }
8096
+ );
8202
8097
  var AgentAssetSearchResponseSchema = external_exports.object({
8203
8098
  query: external_exports.string(),
8204
8099
  results: external_exports.array(FileCardSchema),
@@ -8373,7 +8268,7 @@ var filesContract = c2.router({
8373
8268
  searchAgentAssets: {
8374
8269
  method: "GET",
8375
8270
  path: "/files/agent-search",
8376
- query: AgentAssetSearchQuerySchema,
8271
+ query: AgentAssetSearchRequestSchema,
8377
8272
  responses: {
8378
8273
  200: AgentAssetSearchResponseSchema,
8379
8274
  403: ErrorBodySchema
@@ -9135,11 +9030,10 @@ var AUTO_MODEL_REGISTRY = [
9135
9030
  {
9136
9031
  nodeType: "video",
9137
9032
  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" }
9033
+ // Kling Avatar does not accept end_image_url, so route frame+audio
9034
+ // runs through LTX's audio-capable image-to-video variant.
9035
+ standard: { model: "ltx-2.3", variant: "image-to-video" },
9036
+ fast: { model: "ltx-2.3", variant: "image-to-video" }
9143
9037
  },
9144
9038
  {
9145
9039
  nodeType: "video",
@@ -9230,7 +9124,11 @@ var AutoModelProxy = class {
9230
9124
  resolve(input) {
9231
9125
  switch (input.nodeType) {
9232
9126
  case "video":
9233
- return this.resolveVideo(input.connectedInputs, input.tier);
9127
+ return this.resolveVideo(
9128
+ input.connectedInputs,
9129
+ input.tier,
9130
+ input.canvasVideoInputRoutingEnabled === true
9131
+ );
9234
9132
  case "image":
9235
9133
  return this.resolveImage(input.connectedInputs, input.tier);
9236
9134
  case "text":
@@ -9249,7 +9147,7 @@ var AutoModelProxy = class {
9249
9147
  return this.registry;
9250
9148
  }
9251
9149
  // ── Private resolvers ───────────────────────────────────────────────
9252
- resolveVideo(ci, tier) {
9150
+ resolveVideo(ci, tier, canvasVideoInputRoutingEnabled) {
9253
9151
  const hasAnyImage = ci.hasImage || ci.hasStartingImage || ci.hasEndImage || ci.hasReferenceImage;
9254
9152
  if (ci.hasVideo && hasAnyImage) {
9255
9153
  return {
@@ -9273,7 +9171,11 @@ var AutoModelProxy = class {
9273
9171
  return this.lookupRoute("video", "video-with-audio", tier);
9274
9172
  }
9275
9173
  if (ci.hasAudio && (ci.hasStartingImage || ci.hasImage) && ci.hasEndImage) {
9276
- return this.lookupRoute("video", "frames-with-audio", tier);
9174
+ return canvasVideoInputRoutingEnabled ? this.lookupRoute("video", "frames-with-audio", tier) : {
9175
+ valid: true,
9176
+ model: "ltx-2.3-audio",
9177
+ variant: "image-to-video"
9178
+ };
9277
9179
  }
9278
9180
  if (ci.hasAudio && hasAnyImage) {
9279
9181
  return this.lookupRoute("video", "image-with-audio", tier);
@@ -9423,6 +9325,7 @@ var GENERATION_MODES = [
9423
9325
  "video-to-video",
9424
9326
  "reference-to-video",
9425
9327
  "first-last-frame-to-video",
9328
+ "audio-to-video",
9426
9329
  "text-to-speech",
9427
9330
  "audio-to-audio",
9428
9331
  "text-to-sfx",
@@ -9809,6 +9712,181 @@ var ElevenLabsVoiceSettingsSchema = external_exports.object({
9809
9712
  outputFormat: ElevenLabsOutputFormatSchema.optional()
9810
9713
  });
9811
9714
 
9715
+ // ../api-contract/src/lora.ts
9716
+ var c6 = initContract();
9717
+ var LORA_UPLOAD_PART_SIZE_BYTES = 16 * 1024 * 1024;
9718
+ var MAX_LORA_NAME_LENGTH = 80;
9719
+ var MAX_LORA_DESCRIPTION_LENGTH = 1e3;
9720
+ var MAX_LORA_TRIGGER_WORDS_LENGTH = 500;
9721
+ var MAX_LORA_FILE_SIZE_BYTES = 2 * 1024 * 1024 * 1024;
9722
+ var MAX_LORAS_PER_GENERATION = 5;
9723
+ var MIN_LORA_WEIGHT = 0;
9724
+ var MAX_LORA_WEIGHT = 1.5;
9725
+ var MIN_SDXL_STEPS = 1;
9726
+ var MAX_SDXL_STEPS = 50;
9727
+ var MIN_SDXL_GUIDANCE_SCALE = 0;
9728
+ var MAX_SDXL_GUIDANCE_SCALE = 20;
9729
+ var LoraBaseModelSchema = external_exports.enum(["sdxl", "flux", "flux-2", "other"]);
9730
+ var LoraSelectionSchema = external_exports.object({
9731
+ id: external_exports.string().uuid(),
9732
+ weight: external_exports.number().min(MIN_LORA_WEIGHT).max(MAX_LORA_WEIGHT)
9733
+ });
9734
+ var LoraStackSchema = external_exports.array(LoraSelectionSchema).max(MAX_LORAS_PER_GENERATION);
9735
+ var AppliedLoraSchema = external_exports.object({
9736
+ id: external_exports.string(),
9737
+ name: external_exports.string(),
9738
+ weight: external_exports.number()
9739
+ });
9740
+ var SdxlStepsSchema = external_exports.number().int().min(MIN_SDXL_STEPS).max(MAX_SDXL_STEPS);
9741
+ var SdxlGuidanceScaleSchema = external_exports.number().min(MIN_SDXL_GUIDANCE_SCALE).max(MAX_SDXL_GUIDANCE_SCALE);
9742
+ var LoraSummarySchema = external_exports.object({
9743
+ id: external_exports.string().uuid(),
9744
+ name: external_exports.string(),
9745
+ description: external_exports.string(),
9746
+ /** Activation tokens parsed from the file (best-effort); null if none. */
9747
+ triggerWords: external_exports.string().nullable(),
9748
+ baseModel: LoraBaseModelSchema,
9749
+ /** Recommended blend weight the picker pre-fills. */
9750
+ defaultScale: external_exports.number(),
9751
+ previewUrl: external_exports.string().nullable(),
9752
+ /** teamId is null ⇒ a global/curated LoRA owned by Melius (added by us). */
9753
+ isGlobal: external_exports.boolean(),
9754
+ fileSizeBytes: external_exports.number(),
9755
+ createdAt: external_exports.string(),
9756
+ updatedAt: external_exports.string()
9757
+ });
9758
+ var ListLorasResponseSchema = external_exports.object({
9759
+ loras: external_exports.array(LoraSummarySchema)
9760
+ });
9761
+ var InitLoraUploadBodySchema = external_exports.object({
9762
+ filename: external_exports.string().min(1),
9763
+ sizeInBytes: external_exports.number().int().positive().max(MAX_LORA_FILE_SIZE_BYTES)
9764
+ });
9765
+ var InitLoraUploadResponseSchema = external_exports.object({
9766
+ /** S3 multipart upload id the client threads into sign-part + create. */
9767
+ uploadId: external_exports.string(),
9768
+ s3Bucket: external_exports.string(),
9769
+ s3Key: external_exports.string(),
9770
+ /** Byte size of each part except the last (see LORA_UPLOAD_PART_SIZE_BYTES). */
9771
+ partSize: external_exports.number().int().positive()
9772
+ });
9773
+ var SignLoraUploadPartBodySchema = external_exports.object({
9774
+ uploadId: external_exports.string().min(1),
9775
+ s3Bucket: external_exports.string().min(1),
9776
+ s3Key: external_exports.string().min(1),
9777
+ partNumber: external_exports.number().int().positive()
9778
+ });
9779
+ var SignLoraUploadPartResponseSchema = external_exports.object({
9780
+ /** Short-lived presigned PUT for this one part. */
9781
+ url: external_exports.string()
9782
+ });
9783
+ var CreateLoraBodySchema = external_exports.object({
9784
+ name: external_exports.string().trim().min(1).max(MAX_LORA_NAME_LENGTH),
9785
+ description: external_exports.string().max(MAX_LORA_DESCRIPTION_LENGTH).optional(),
9786
+ /** Uploader override; server falls back to the parsed metadata when omitted. */
9787
+ triggerWords: external_exports.string().max(MAX_LORA_TRIGGER_WORDS_LENGTH).optional(),
9788
+ baseModel: LoraBaseModelSchema,
9789
+ s3Bucket: external_exports.string().min(1),
9790
+ s3Key: external_exports.string().min(1),
9791
+ /** Multipart upload id + parts to finalize before validating the object. */
9792
+ uploadId: external_exports.string().min(1),
9793
+ parts: external_exports.array(CompletedPartSchema).min(1),
9794
+ /**
9795
+ * Optional client-attested SHA-256 (integrity reference, never verified).
9796
+ * Omitted for large files — hashing gigabytes in the browser OOMs the tab —
9797
+ * where S3 multipart part ETags already guarantee integrity.
9798
+ */
9799
+ sha256: external_exports.string().optional(),
9800
+ fileSizeBytes: external_exports.number().int().positive().max(MAX_LORA_FILE_SIZE_BYTES),
9801
+ defaultScale: external_exports.number().min(MIN_LORA_WEIGHT).max(MAX_LORA_WEIGHT).optional()
9802
+ });
9803
+ var UpdateLoraBodySchema = external_exports.object({
9804
+ name: external_exports.string().trim().min(1).max(MAX_LORA_NAME_LENGTH).optional(),
9805
+ description: external_exports.string().max(MAX_LORA_DESCRIPTION_LENGTH).optional(),
9806
+ triggerWords: external_exports.string().max(MAX_LORA_TRIGGER_WORDS_LENGTH).optional()
9807
+ });
9808
+ var ForbiddenSchema = MessageResponseSchema;
9809
+ var NotFoundSchema = MessageResponseSchema;
9810
+ var BadRequestSchema = MessageResponseSchema;
9811
+ var ConflictSchema = MessageResponseSchema;
9812
+ var loraContract = c6.router({
9813
+ listLoras: {
9814
+ method: "GET",
9815
+ path: "/teams/:teamId/loras",
9816
+ pathParams: external_exports.object({ teamId: external_exports.string().uuid() }),
9817
+ responses: {
9818
+ 200: ListLorasResponseSchema,
9819
+ 403: ForbiddenSchema,
9820
+ 404: NotFoundSchema
9821
+ }
9822
+ },
9823
+ // @SkipManagedTransaction on the handler — only starts an S3 multipart.
9824
+ initLoraUpload: {
9825
+ method: "POST",
9826
+ path: "/teams/:teamId/loras/upload",
9827
+ pathParams: external_exports.object({ teamId: external_exports.string().uuid() }),
9828
+ body: InitLoraUploadBodySchema,
9829
+ responses: {
9830
+ 200: InitLoraUploadResponseSchema,
9831
+ 400: BadRequestSchema,
9832
+ 403: ForbiddenSchema
9833
+ }
9834
+ },
9835
+ // @SkipManagedTransaction on the handler — only mints a presigned part PUT.
9836
+ signLoraUploadPart: {
9837
+ method: "POST",
9838
+ path: "/teams/:teamId/loras/upload/sign-part",
9839
+ pathParams: external_exports.object({ teamId: external_exports.string().uuid() }),
9840
+ body: SignLoraUploadPartBodySchema,
9841
+ responses: {
9842
+ 200: SignLoraUploadPartResponseSchema,
9843
+ 400: BadRequestSchema,
9844
+ 403: ForbiddenSchema
9845
+ }
9846
+ },
9847
+ createLora: {
9848
+ method: "POST",
9849
+ path: "/teams/:teamId/loras",
9850
+ pathParams: external_exports.object({ teamId: external_exports.string().uuid() }),
9851
+ body: CreateLoraBodySchema,
9852
+ responses: {
9853
+ 201: LoraSummarySchema,
9854
+ 400: BadRequestSchema,
9855
+ 403: ForbiddenSchema,
9856
+ 409: ConflictSchema
9857
+ }
9858
+ },
9859
+ updateLora: {
9860
+ method: "PATCH",
9861
+ path: "/teams/:teamId/loras/:loraId",
9862
+ pathParams: external_exports.object({
9863
+ teamId: external_exports.string().uuid(),
9864
+ loraId: external_exports.string().uuid()
9865
+ }),
9866
+ body: UpdateLoraBodySchema,
9867
+ responses: {
9868
+ 200: LoraSummarySchema,
9869
+ 400: BadRequestSchema,
9870
+ 403: ForbiddenSchema,
9871
+ 404: NotFoundSchema,
9872
+ 409: ConflictSchema
9873
+ }
9874
+ },
9875
+ deleteLora: {
9876
+ method: "DELETE",
9877
+ path: "/teams/:teamId/loras/:loraId",
9878
+ pathParams: external_exports.object({
9879
+ teamId: external_exports.string().uuid(),
9880
+ loraId: external_exports.string().uuid()
9881
+ }),
9882
+ responses: {
9883
+ 200: external_exports.object({ id: external_exports.string().uuid() }),
9884
+ 403: ForbiddenSchema,
9885
+ 404: NotFoundSchema
9886
+ }
9887
+ }
9888
+ });
9889
+
9812
9890
  // ../api-contract/src/preview-asset.ts
9813
9891
  var PreviewAssetSchema = external_exports.object({
9814
9892
  url: external_exports.string(),
@@ -9917,7 +9995,7 @@ var TemplatePortSummarySchema = external_exports.object({
9917
9995
  });
9918
9996
 
9919
9997
  // ../api-contract/src/canvas.ts
9920
- var c6 = initContract();
9998
+ var c7 = initContract();
9921
9999
  var CanvasResponseSchema = external_exports.object({
9922
10000
  id: external_exports.string().uuid(),
9923
10001
  teamId: external_exports.string().uuid(),
@@ -9975,7 +10053,13 @@ var ModelConfigSchema = external_exports.object({
9975
10053
  font: external_exports.object({
9976
10054
  source: external_exports.enum(["custom", "google"]),
9977
10055
  familyName: external_exports.string()
9978
- }).optional()
10056
+ }).optional(),
10057
+ /** SDXL only: the LoRA stack applied to this version (name denormalized so history survives deletion). */
10058
+ loras: external_exports.array(AppliedLoraSchema).optional(),
10059
+ /** SDXL only: sampler steps used for this version. */
10060
+ steps: external_exports.number().optional(),
10061
+ /** SDXL only: classifier-free guidance scale used for this version. */
10062
+ guidanceScale: external_exports.number().optional()
9979
10063
  });
9980
10064
  var ModelConfigInputSchema = external_exports.object({
9981
10065
  model: external_exports.string(),
@@ -10369,7 +10453,17 @@ var RunNodeBodySchema = external_exports.object({
10369
10453
  * ElevenLabs voice tuning for TTS / voice-changer runs. Applied when
10370
10454
  * provided; the `voice-granular-controls` flag only gates the toolbar UI.
10371
10455
  */
10372
- elevenLabsVoiceSettings: ElevenLabsVoiceSettingsSchema.optional()
10456
+ elevenLabsVoiceSettings: ElevenLabsVoiceSettingsSchema.optional(),
10457
+ /**
10458
+ * LoRA adapters to stack on a self-hosted SDXL generation (`{ id, weight }`).
10459
+ * The server resolves each id to a presigned weights URL and validates team
10460
+ * access. Only honored on the SDXL model.
10461
+ */
10462
+ loras: LoraStackSchema.optional(),
10463
+ /** SDXL sampler steps. Only honored on the SDXL model. */
10464
+ steps: SdxlStepsSchema.optional(),
10465
+ /** SDXL guidance scale. Only honored on the SDXL model. */
10466
+ guidanceScale: SdxlGuidanceScaleSchema.optional()
10373
10467
  });
10374
10468
  var RunImageToolBodySchema = external_exports.object({
10375
10469
  canvasId: external_exports.string().uuid().optional(),
@@ -10798,7 +10892,7 @@ var CanvasSettingsResponseSchema = external_exports.object({
10798
10892
  createdAt: external_exports.string().datetime(),
10799
10893
  updatedAt: external_exports.string().datetime()
10800
10894
  });
10801
- var canvasContract = c6.router({
10895
+ var canvasContract = c7.router({
10802
10896
  createCanvas: {
10803
10897
  method: "POST",
10804
10898
  path: "/projects/:projectId/canvases",
@@ -10852,7 +10946,7 @@ var canvasContract = c6.router({
10852
10946
  canvasId: external_exports.string().uuid()
10853
10947
  }),
10854
10948
  body: null,
10855
- responses: { 204: c6.noBody() }
10949
+ responses: { 204: c7.noBody() }
10856
10950
  },
10857
10951
  duplicateCanvas: {
10858
10952
  method: "POST",
@@ -11267,7 +11361,7 @@ var canvasContract = c6.router({
11267
11361
  pathParams: external_exports.object({ nodeRunId: external_exports.string().uuid() }),
11268
11362
  body: external_exports.object({}),
11269
11363
  responses: {
11270
- 204: c6.noBody(),
11364
+ 204: c7.noBody(),
11271
11365
  404: NodeRunErrorResponseSchema,
11272
11366
  409: NodeRunErrorResponseSchema
11273
11367
  },
@@ -11445,7 +11539,7 @@ var canvasContract = c6.router({
11445
11539
  path: "/comment-threads/:threadId",
11446
11540
  pathParams: external_exports.object({ threadId: external_exports.string().uuid() }),
11447
11541
  body: null,
11448
- responses: { 204: c6.noBody() }
11542
+ responses: { 204: c7.noBody() }
11449
11543
  },
11450
11544
  // ── Comments ────────────────────────────────────────────────────────
11451
11545
  addComment: {
@@ -11460,7 +11554,7 @@ var canvasContract = c6.router({
11460
11554
  path: "/comments/:commentId",
11461
11555
  pathParams: external_exports.object({ commentId: external_exports.string().uuid() }),
11462
11556
  body: null,
11463
- responses: { 204: c6.noBody() }
11557
+ responses: { 204: c7.noBody() }
11464
11558
  },
11465
11559
  // ── Direct CRDT Mutations (CLI/Agent) ───────────────────────────────
11466
11560
  // These endpoints write directly to the Y.Doc (CRDT) for the canvas.
@@ -11500,7 +11594,7 @@ var canvasContract = c6.router({
11500
11594
  }),
11501
11595
  body: null,
11502
11596
  responses: {
11503
- 204: c6.noBody(),
11597
+ 204: c7.noBody(),
11504
11598
  404: MessageResponseSchema,
11505
11599
  409: MessageResponseSchema
11506
11600
  }
@@ -11536,7 +11630,7 @@ var canvasContract = c6.router({
11536
11630
  }),
11537
11631
  body: null,
11538
11632
  responses: {
11539
- 204: c6.noBody(),
11633
+ 204: c7.noBody(),
11540
11634
  404: MessageResponseSchema,
11541
11635
  409: MessageResponseSchema
11542
11636
  }
@@ -11629,7 +11723,7 @@ var canvasContract = c6.router({
11629
11723
  }),
11630
11724
  body: null,
11631
11725
  responses: {
11632
- 204: c6.noBody(),
11726
+ 204: c7.noBody(),
11633
11727
  403: MessageResponseSchema,
11634
11728
  404: MessageResponseSchema,
11635
11729
  409: MessageResponseSchema
@@ -11675,6 +11769,35 @@ var canvasContract = c6.router({
11675
11769
  404: MessageResponseSchema
11676
11770
  }
11677
11771
  },
11772
+ // Server-side placement of existing library assets onto a canvas, by id.
11773
+ // Unlike `linkLibraryAsset` (which links an asset to a node the in-app
11774
+ // client already created in the CRDT), this CREATES the file node(s)
11775
+ // server-side and references the ORIGINAL asset (no duplicate) — the path
11776
+ // MCP/CLI need, since they have no browser canvas store. Positions are
11777
+ // planned server-side into open space. Gated by the same asset-search
11778
+ // entitlement (`isAgentSearchToolAvailable`) as `searchAgentAssets`.
11779
+ placeLibraryAssets: {
11780
+ method: "POST",
11781
+ path: "/canvases/:canvasId/library-assets/place",
11782
+ pathParams: external_exports.object({ canvasId: external_exports.string().uuid() }),
11783
+ body: external_exports.object({
11784
+ assetIds: external_exports.array(external_exports.string().uuid()).min(1).max(10)
11785
+ }),
11786
+ responses: {
11787
+ 200: external_exports.object({
11788
+ placed: external_exports.array(
11789
+ external_exports.object({
11790
+ assetId: external_exports.string().uuid(),
11791
+ nodeId: external_exports.string().uuid(),
11792
+ mediaType: external_exports.enum(["image", "video", "audio", "pdf"])
11793
+ })
11794
+ )
11795
+ }),
11796
+ 403: external_exports.object({ message: external_exports.string() }),
11797
+ 404: external_exports.object({ message: external_exports.string() })
11798
+ },
11799
+ summary: "Place library assets onto a canvas"
11800
+ },
11678
11801
  // ── Canvas Settings ──────────────────────────────────────────────
11679
11802
  getCanvasSettings: {
11680
11803
  method: "GET",
@@ -11713,7 +11836,7 @@ var canvasContract = c6.router({
11713
11836
  path: "/canvases/:canvasId/settings",
11714
11837
  pathParams: external_exports.object({ canvasId: external_exports.string().uuid() }),
11715
11838
  body: null,
11716
- responses: { 204: c6.noBody() }
11839
+ responses: { 204: c7.noBody() }
11717
11840
  },
11718
11841
  // ── Agent presence ──────────────────────────────────────────────────
11719
11842
  // Broadcast an agent's "claim region" to all connected canvas
@@ -11740,16 +11863,16 @@ var canvasContract = c6.router({
11740
11863
  }).nullable().optional(),
11741
11864
  status: external_exports.string().min(1).max(80)
11742
11865
  }).refine(
11743
- (c16) => c16.nodeIds.length > 0 || c16.bounds != null,
11866
+ (c17) => c17.nodeIds.length > 0 || c17.bounds != null,
11744
11867
  "claim must include nodeIds or bounds"
11745
11868
  ).nullable()
11746
11869
  }),
11747
- responses: { 204: c6.noBody() }
11870
+ responses: { 204: c7.noBody() }
11748
11871
  }
11749
11872
  });
11750
11873
 
11751
11874
  // ../api-contract/src/custom-font.ts
11752
- var c7 = initContract();
11875
+ var c8 = initContract();
11753
11876
  var CustomFontResponseSchema = external_exports.object({
11754
11877
  id: external_exports.string().uuid(),
11755
11878
  familyName: external_exports.string(),
@@ -11773,10 +11896,16 @@ var CompleteCustomFontUploadBodySchema = external_exports.object({
11773
11896
  sizeInBytes: external_exports.number().int().positive(),
11774
11897
  parts: external_exports.array(CompletedPartSchema)
11775
11898
  });
11776
- var customFontContract = c7.router({
11899
+ var ListCustomFontsQuerySchema = external_exports.object({
11900
+ // Optional name filter (matches family name or original filename). Used by
11901
+ // the Assets page fonts view; the node font pickers omit it to list all.
11902
+ search: external_exports.string().trim().optional()
11903
+ });
11904
+ var customFontContract = c8.router({
11777
11905
  list: {
11778
11906
  method: "GET",
11779
11907
  path: "/custom-fonts",
11908
+ query: ListCustomFontsQuerySchema,
11780
11909
  responses: { 200: ListCustomFontsResponseSchema },
11781
11910
  summary: "List custom fonts for the team"
11782
11911
  },
@@ -11801,7 +11930,7 @@ var customFontContract = c7.router({
11801
11930
  });
11802
11931
 
11803
11932
  // ../api-contract/src/generation.ts
11804
- var c8 = initContract();
11933
+ var c9 = initContract();
11805
11934
  var GenerationJobStatusSchema = external_exports.enum([
11806
11935
  "in_progress",
11807
11936
  "finished",
@@ -11859,7 +11988,21 @@ var GenerationRunBodySchema = external_exports.object({
11859
11988
  * by every other model. When absent, the server falls back to a
11860
11989
  * default Google font (Inter).
11861
11990
  */
11862
- font: BetterFontSelectionSchema.optional()
11991
+ font: BetterFontSelectionSchema.optional(),
11992
+ /**
11993
+ * LoRA adapters to stack on a LoRA-capable image generation (`{ id, weight }`;
11994
+ * the server resolves each id to a presigned weights URL). Only honored on
11995
+ * LoRA-capable models (SDXL on Modal, Flux on fal); ignored by every other
11996
+ * model.
11997
+ */
11998
+ loras: LoraStackSchema.optional(),
11999
+ /** Sampler steps. Only honored on LoRA-capable models (SDXL, Flux). */
12000
+ steps: SdxlStepsSchema.optional(),
12001
+ /**
12002
+ * Classifier-free guidance scale. Only honored on LoRA-capable models
12003
+ * (SDXL, Flux).
12004
+ */
12005
+ guidanceScale: SdxlGuidanceScaleSchema.optional()
11863
12006
  });
11864
12007
  var ModelCategorySchema = external_exports.enum([
11865
12008
  "image",
@@ -12014,6 +12157,14 @@ var ModelVariantResponseSchema = external_exports.object({
12014
12157
  maxReferenceImagesWithEndImage: external_exports.number().nullable().optional(),
12015
12158
  maxVideoInputs: external_exports.number().nullable().optional(),
12016
12159
  maxAudioInputs: external_exports.number().nullable().optional(),
12160
+ /**
12161
+ * For variants accepting both video and audio: whether the connected
12162
+ * durations must line up. `false` for lipsync models (e.g. Kling Lipsync)
12163
+ * whose endpoint takes audio independent of the video length, so the
12164
+ * frontend preflight must not block a mismatch. Omitted/null means the
12165
+ * default duration-match requirement applies.
12166
+ */
12167
+ requiresMatchingAudioVideoDuration: external_exports.boolean().nullable().optional(),
12017
12168
  maxNativeOutputs: external_exports.number().nullable(),
12018
12169
  maxInputArea: external_exports.number().nullable(),
12019
12170
  inputImageFileSizeConstraints: InputFileSizeConstraintsResponseSchema.nullable().optional(),
@@ -12036,7 +12187,13 @@ var ModelVariantResponseSchema = external_exports.object({
12036
12187
  estimatedLatencySeconds: external_exports.number().nullable().optional(),
12037
12188
  defaultResolution: OutputResolutionSchema.nullable().optional(),
12038
12189
  featuredDescription: external_exports.string().nullable(),
12039
- cost: ModelCostSchema.optional()
12190
+ cost: ModelCostSchema.optional(),
12191
+ /**
12192
+ * Per-second credit multiplier applied to connected input video duration,
12193
+ * using the resolution-adjusted output per-second price as the base. Lets
12194
+ * the cost preview include the input-video surcharge that the server bills.
12195
+ */
12196
+ inputVideoCreditMultiplier: external_exports.number().optional()
12040
12197
  });
12041
12198
  var ModelResponseSchema = external_exports.object({
12042
12199
  name: external_exports.string(),
@@ -12112,7 +12269,7 @@ var SeedancePrecheckResponseSchema = external_exports.object({
12112
12269
  reason: external_exports.string(),
12113
12270
  cacheKey: external_exports.string()
12114
12271
  });
12115
- var generationContract = c8.router({
12272
+ var generationContract = c9.router({
12116
12273
  listGenerativeModels: {
12117
12274
  method: "GET",
12118
12275
  path: "/generation/models",
@@ -12171,7 +12328,7 @@ var generationContract = c8.router({
12171
12328
  until: external_exports.string().datetime().optional()
12172
12329
  }),
12173
12330
  responses: {
12174
- 200: c8.otherResponse({
12331
+ 200: c9.otherResponse({
12175
12332
  contentType: "text/csv",
12176
12333
  body: external_exports.string()
12177
12334
  })
@@ -12198,7 +12355,7 @@ var generationContract = c8.router({
12198
12355
  });
12199
12356
 
12200
12357
  // ../api-contract/src/presets.ts
12201
- var c9 = initContract();
12358
+ var c10 = initContract();
12202
12359
  var TextNodeDataSchema = external_exports.object({
12203
12360
  title: external_exports.string(),
12204
12361
  mode: external_exports.string(),
@@ -12300,7 +12457,7 @@ var PresetResponseSchema = external_exports.object({
12300
12457
  nodes: external_exports.array(PresetNodeSchema),
12301
12458
  edges: external_exports.array(PresetEdgeSchema)
12302
12459
  });
12303
- var presetsContract = c9.router({
12460
+ var presetsContract = c10.router({
12304
12461
  listPresets: {
12305
12462
  method: "GET",
12306
12463
  path: "/presets",
@@ -12314,7 +12471,7 @@ var presetsContract = c9.router({
12314
12471
  });
12315
12472
 
12316
12473
  // ../api-contract/src/profile.ts
12317
- var c10 = initContract();
12474
+ var c11 = initContract();
12318
12475
  var MagicResizeCustomResolutionSchema = external_exports.object({
12319
12476
  width: external_exports.number().int().min(64).max(4096),
12320
12477
  height: external_exports.number().int().min(64).max(4096)
@@ -12345,7 +12502,7 @@ var ReferralRewardNotificationSchema = external_exports.object({
12345
12502
  counterpartName: external_exports.string(),
12346
12503
  redeemedAt: external_exports.string().datetime()
12347
12504
  });
12348
- var profileContract = c10.router({
12505
+ var profileContract = c11.router({
12349
12506
  getMyProfile: {
12350
12507
  method: "GET",
12351
12508
  path: "/profile/me",
@@ -12404,7 +12561,7 @@ var profileContract = c10.router({
12404
12561
  });
12405
12562
 
12406
12563
  // ../api-contract/src/share.ts
12407
- var c11 = initContract();
12564
+ var c12 = initContract();
12408
12565
  var ShareModeSchema = external_exports.enum(["restricted", "view"]);
12409
12566
  var ShareCanvasResponseSchema = external_exports.object({
12410
12567
  id: external_exports.string().uuid(),
@@ -12440,7 +12597,7 @@ var ShareProjectInfoResponseSchema = external_exports.object({
12440
12597
  avatarUrl: external_exports.string().nullable()
12441
12598
  }).nullable()
12442
12599
  });
12443
- var shareContract = c11.router({
12600
+ var shareContract = c12.router({
12444
12601
  // Public endpoint — no auth required
12445
12602
  getSharedProjectInfo: {
12446
12603
  method: "GET",
@@ -12527,7 +12684,7 @@ var shareContract = c11.router({
12527
12684
  });
12528
12685
 
12529
12686
  // ../api-contract/src/team.ts
12530
- var c12 = initContract();
12687
+ var c13 = initContract();
12531
12688
  var TeamRoleSchema = external_exports.enum(["owner", "admin", "editor", "viewer"]);
12532
12689
  var TeamPlanSchema = external_exports.enum([
12533
12690
  "TIER_0",
@@ -12558,6 +12715,34 @@ var EnterpriseStripeBillingInputSchema = external_exports.object({
12558
12715
  path: ["contractEndAt"]
12559
12716
  }
12560
12717
  );
12718
+ var CustomPlanCreditRolloverPolicySchema = external_exports.discriminatedUnion(
12719
+ "mode",
12720
+ [
12721
+ external_exports.object({ mode: external_exports.literal("none") }),
12722
+ external_exports.object({ mode: external_exports.literal("unlimited") }),
12723
+ external_exports.object({
12724
+ mode: external_exports.literal("months"),
12725
+ months: external_exports.number().int().nonnegative()
12726
+ }),
12727
+ external_exports.object({
12728
+ mode: external_exports.literal("percentage"),
12729
+ percentage: external_exports.number().int().min(0).max(100)
12730
+ })
12731
+ ]
12732
+ );
12733
+ var EnterpriseOrderDetailsSchema = external_exports.object({
12734
+ orderType: external_exports.string().min(1),
12735
+ customPlanType: CustomPlanTypeSchema.optional(),
12736
+ creditPackageAmount: external_exports.number().int().nonnegative(),
12737
+ creditPackageUnit: external_exports.string().min(1),
12738
+ creditRolloverPolicy: CustomPlanCreditRolloverPolicySchema.optional(),
12739
+ orderDate: external_exports.string().date(),
12740
+ /** Omit for plans with no fixed contract term (e.g. monthly Stripe plans). */
12741
+ termCount: external_exports.number().int().positive().optional(),
12742
+ termUnit: external_exports.string().min(1).optional(),
12743
+ lineItems: external_exports.array(EnterpriseOrderLineItemSchema),
12744
+ totalContractValue: external_exports.string().min(1)
12745
+ });
12561
12746
  var EnterprisePendingExpansionSchema = external_exports.object({
12562
12747
  /** Stable id minted at prepare; also the credit-grant idempotency key. */
12563
12748
  id: external_exports.string(),
@@ -12571,6 +12756,13 @@ var EnterprisePendingExpansionSchema = external_exports.object({
12571
12756
  /** New contract end when the operator chose a new term; absent means keep the existing term. */
12572
12757
  contractEndAt: external_exports.string().datetime().optional(),
12573
12758
  keepContractEnd: external_exports.boolean(),
12759
+ /**
12760
+ * Operator-authored replacement order details (line items, total contract
12761
+ * value, credit package, term) applied to the stored order when the customer
12762
+ * activates. Absent keeps the existing terms. Purely presentational — the
12763
+ * actual charge/credits come from the billing figures above.
12764
+ */
12765
+ newOrderDetails: EnterpriseOrderDetailsSchema.optional(),
12574
12766
  preparedAt: external_exports.string().datetime(),
12575
12767
  preparedByEmail: external_exports.string().nullable()
12576
12768
  });
@@ -12606,33 +12798,7 @@ var EnterpriseStripeBillingAuditEntrySchema = external_exports.object({
12606
12798
  previousStripePriceId: external_exports.string().optional(),
12607
12799
  notes: external_exports.string().optional()
12608
12800
  });
12609
- var CustomPlanCreditRolloverPolicySchema = external_exports.discriminatedUnion(
12610
- "mode",
12611
- [
12612
- external_exports.object({ mode: external_exports.literal("none") }),
12613
- external_exports.object({ mode: external_exports.literal("unlimited") }),
12614
- external_exports.object({
12615
- mode: external_exports.literal("months"),
12616
- months: external_exports.number().int().nonnegative()
12617
- }),
12618
- external_exports.object({
12619
- mode: external_exports.literal("percentage"),
12620
- percentage: external_exports.number().int().min(0).max(100)
12621
- })
12622
- ]
12623
- );
12624
- var EnterpriseOrderSchema = external_exports.object({
12625
- orderType: external_exports.string().min(1),
12626
- customPlanType: CustomPlanTypeSchema.optional(),
12627
- creditPackageAmount: external_exports.number().int().nonnegative(),
12628
- creditPackageUnit: external_exports.string().min(1),
12629
- creditRolloverPolicy: CustomPlanCreditRolloverPolicySchema.optional(),
12630
- orderDate: external_exports.string().date(),
12631
- /** Omit for plans with no fixed contract term (e.g. monthly Stripe plans). */
12632
- termCount: external_exports.number().int().positive().optional(),
12633
- termUnit: external_exports.string().min(1).optional(),
12634
- lineItems: external_exports.array(EnterpriseOrderLineItemSchema),
12635
- totalContractValue: external_exports.string().min(1),
12801
+ var EnterpriseOrderSchema = EnterpriseOrderDetailsSchema.extend({
12636
12802
  stripeBilling: EnterpriseStripeBillingInputSchema.optional()
12637
12803
  });
12638
12804
  var CustomerEnterpriseOrderSchema = EnterpriseOrderSchema.extend({
@@ -12741,7 +12907,7 @@ var EmailInviteSchema = external_exports.object({
12741
12907
  senderName: external_exports.string(),
12742
12908
  createdAt: external_exports.string().datetime()
12743
12909
  });
12744
- var teamContract = c12.router({
12910
+ var teamContract = c13.router({
12745
12911
  createTeam: {
12746
12912
  method: "POST",
12747
12913
  path: "/teams",
@@ -12806,7 +12972,7 @@ var teamContract = c12.router({
12806
12972
  method: "DELETE",
12807
12973
  path: "/teams/current",
12808
12974
  body: null,
12809
- responses: { 204: c12.noBody() }
12975
+ responses: { 204: c13.noBody() }
12810
12976
  },
12811
12977
  listMembers: {
12812
12978
  method: "GET",
@@ -12827,7 +12993,7 @@ var teamContract = c12.router({
12827
12993
  path: "/teams/current/members/:userId",
12828
12994
  pathParams: external_exports.object({ userId: external_exports.string().uuid() }),
12829
12995
  body: null,
12830
- responses: { 204: c12.noBody() }
12996
+ responses: { 204: c13.noBody() }
12831
12997
  },
12832
12998
  getShareableLink: {
12833
12999
  method: "GET",
@@ -12868,7 +13034,7 @@ var teamContract = c12.router({
12868
13034
  path: "/teams/current/invite-email/:inviteId",
12869
13035
  pathParams: external_exports.object({ inviteId: external_exports.string().uuid() }),
12870
13036
  body: null,
12871
- responses: { 204: c12.noBody() }
13037
+ responses: { 204: c13.noBody() }
12872
13038
  },
12873
13039
  listJoinRequests: {
12874
13040
  method: "GET",
@@ -12887,7 +13053,7 @@ var teamContract = c12.router({
12887
13053
  path: "/teams/current/join-requests/:requestId",
12888
13054
  pathParams: external_exports.object({ requestId: external_exports.string().uuid() }),
12889
13055
  body: null,
12890
- responses: { 204: c12.noBody() }
13056
+ responses: { 204: c13.noBody() }
12891
13057
  },
12892
13058
  updateDomainJoinSettings: {
12893
13059
  method: "PATCH",
@@ -12909,7 +13075,7 @@ var teamContract = c12.router({
12909
13075
  });
12910
13076
 
12911
13077
  // ../api-contract/src/project.ts
12912
- var c13 = initContract();
13078
+ var c14 = initContract();
12913
13079
  var CanvasSummarySchema = external_exports.object({
12914
13080
  id: external_exports.string().uuid(),
12915
13081
  title: external_exports.string(),
@@ -13008,7 +13174,7 @@ var ProjectSettingsResponseSchema = external_exports.object({
13008
13174
  createdAt: external_exports.string().datetime(),
13009
13175
  updatedAt: external_exports.string().datetime()
13010
13176
  });
13011
- var projectContract = c13.router({
13177
+ var projectContract = c14.router({
13012
13178
  createProject: {
13013
13179
  method: "POST",
13014
13180
  path: "/projects",
@@ -13045,14 +13211,14 @@ var projectContract = c13.router({
13045
13211
  path: "/projects/:projectId",
13046
13212
  pathParams: ProjectIdParamsSchema,
13047
13213
  body: null,
13048
- responses: { 204: c13.noBody() }
13214
+ responses: { 204: c14.noBody() }
13049
13215
  },
13050
13216
  recordProjectView: {
13051
13217
  method: "POST",
13052
13218
  path: "/projects/:projectId/view",
13053
13219
  pathParams: ProjectIdParamsSchema,
13054
13220
  body: null,
13055
- responses: { 204: c13.noBody() }
13221
+ responses: { 204: c14.noBody() }
13056
13222
  },
13057
13223
  listRecentProjects: {
13058
13224
  method: "GET",
@@ -13152,12 +13318,12 @@ var projectContract = c13.router({
13152
13318
  path: "/projects/:projectId/settings",
13153
13319
  pathParams: ProjectIdParamsSchema,
13154
13320
  body: null,
13155
- responses: { 204: c13.noBody() }
13321
+ responses: { 204: c14.noBody() }
13156
13322
  }
13157
13323
  });
13158
13324
 
13159
13325
  // ../api-contract/src/upload.ts
13160
- var c14 = initContract();
13326
+ var c15 = initContract();
13161
13327
  var InitUploadBodySchema = external_exports.object({
13162
13328
  nodeId: external_exports.string().uuid().optional(),
13163
13329
  filename: external_exports.string(),
@@ -13229,7 +13395,7 @@ var CompleteUploadResponseSchema = external_exports.object({
13229
13395
  // clients tolerate deploy skew with older core versions.
13230
13396
  nodeVersionId: external_exports.string().uuid().nullable().optional()
13231
13397
  });
13232
- var uploadContract = c14.router({
13398
+ var uploadContract = c15.router({
13233
13399
  init: {
13234
13400
  method: "POST",
13235
13401
  path: "/upload/init",
@@ -13254,8 +13420,8 @@ var uploadContract = c14.router({
13254
13420
  });
13255
13421
 
13256
13422
  // ../api-contract/src/public/index.ts
13257
- var c15 = initContract();
13258
- var publicContract = c15.router(
13423
+ var c16 = initContract();
13424
+ var publicContract = c16.router(
13259
13425
  {
13260
13426
  project: {
13261
13427
  create: projectContract.createProject,
@@ -13315,6 +13481,13 @@ var publicContract = c15.router(
13315
13481
  signPart: uploadContract.signPart,
13316
13482
  complete: uploadContract.complete
13317
13483
  },
13484
+ asset: {
13485
+ // Semantic/filename search of the team's Files (DAM) library, and
13486
+ // placing a found asset onto a canvas by reference (no duplicate).
13487
+ // Both are gated to Pro + Enterprise by the handlers.
13488
+ search: filesContract.searchAgentAssets,
13489
+ place: canvasContract.placeLibraryAssets
13490
+ },
13318
13491
  model: {
13319
13492
  list: generationContract.listGenerativeModels,
13320
13493
  autoDefaults: generationContract.getAutoModelDefaults
@@ -13548,6 +13721,65 @@ function setConfigValue(key, value) {
13548
13721
  writeConfigFile({ [key]: value });
13549
13722
  }
13550
13723
 
13724
+ // src/lib/update-notice.ts
13725
+ var MEL_CLI_LATEST_VERSION_HEADER = "x-mel-cli-latest-version";
13726
+ var serverLatestVersion = null;
13727
+ function recordServerLatestVersion(value) {
13728
+ if (value) serverLatestVersion = value;
13729
+ }
13730
+ var SEMVER_PATTERN = /^v?\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/;
13731
+ function parseVersion(version) {
13732
+ const trimmed = version.trim();
13733
+ if (!SEMVER_PATTERN.test(trimmed)) return null;
13734
+ const core = trimmed.replace(/^v/, "").split(/[-+]/)[0];
13735
+ const [major, minor, patch] = core.split(".").map((p) => Number.parseInt(p, 10));
13736
+ return [major, minor, patch];
13737
+ }
13738
+ function isNewerVersion(current, latest) {
13739
+ const c17 = parseVersion(current);
13740
+ const l = parseVersion(latest);
13741
+ if (!c17 || !l) return false;
13742
+ for (let i = 0; i < 3; i++) {
13743
+ if (l[i] > c17[i]) return true;
13744
+ if (l[i] < c17[i]) return false;
13745
+ }
13746
+ return false;
13747
+ }
13748
+ function isMajorUpgrade(current, latest) {
13749
+ const c17 = parseVersion(current);
13750
+ const l = parseVersion(latest);
13751
+ if (!c17 || !l) return false;
13752
+ return l[0] > c17[0];
13753
+ }
13754
+ function shouldShowUpgradeNotice(ctx = {}) {
13755
+ const current = ctx.current ?? "0.5.0";
13756
+ const latest = ctx.latest ?? serverLatestVersion;
13757
+ const env = ctx.env ?? process.env;
13758
+ const isTty = ctx.isTty ?? Boolean(process.stderr.isTTY);
13759
+ if (!latest) return false;
13760
+ if (ctx.quiet) return false;
13761
+ if (!isTty) return false;
13762
+ if (env["MEL_NO_UPDATE_NOTIFIER"]) return false;
13763
+ if (env["CI"]) return false;
13764
+ return isNewerVersion(current, latest);
13765
+ }
13766
+ function formatUpgradeNotice(current, latest) {
13767
+ const breaking = isMajorUpgrade(current, latest) ? " This is a new major version that may include breaking changes." : "";
13768
+ return `
13769
+ mel ${latest} is available (you have ${current}).${breaking}
13770
+ Upgrade: npm install -g @melius-ai/cli@latest (or: brew upgrade mel) Silence: MEL_NO_UPDATE_NOTIFIER=1
13771
+ `;
13772
+ }
13773
+ function printUpgradeNoticeIfNeeded(ctx = {}) {
13774
+ try {
13775
+ if (!shouldShowUpgradeNotice(ctx)) return;
13776
+ const current = ctx.current ?? "0.5.0";
13777
+ const latest = ctx.latest ?? serverLatestVersion;
13778
+ process.stderr.write(formatUpgradeNotice(current, latest));
13779
+ } catch {
13780
+ }
13781
+ }
13782
+
13551
13783
  // src/lib/client.ts
13552
13784
  var _client = null;
13553
13785
  var _verbose = false;
@@ -13678,6 +13910,9 @@ function createClient(config) {
13678
13910
  }
13679
13911
  const t0 = _verbose ? Date.now() : 0;
13680
13912
  const response = await fetchWithRateLimitRetry(path4, fetchOpts);
13913
+ recordServerLatestVersion(
13914
+ response.headers.get(MEL_CLI_LATEST_VERSION_HEADER)
13915
+ );
13681
13916
  const contentType = response.headers.get("content-type");
13682
13917
  let responseBody;
13683
13918
  if (contentType?.includes("application/json")) {
@@ -14228,8 +14463,8 @@ function computeAutoformat(allNodes, allEdges, nodeIds) {
14228
14463
  topologyEdges.push({ ...edge, srcNodeId: src, dstNodeId: dst });
14229
14464
  }
14230
14465
  const rawComponents = findConnectedComponents(targetIds, topologyEdges);
14231
- const connectedComponents = rawComponents.filter((c16) => c16.length > 1);
14232
- const isolatedIds = rawComponents.filter((c16) => c16.length === 1).flat().sort();
14466
+ const connectedComponents = rawComponents.filter((c17) => c17.length > 1);
14467
+ const isolatedIds = rawComponents.filter((c17) => c17.length === 1).flat().sort();
14233
14468
  if (isolatedIds.length > 0) connectedComponents.push(isolatedIds);
14234
14469
  const components = connectedComponents.sort((a, b) => {
14235
14470
  const aHasText = a.some((id) => {
@@ -14419,7 +14654,7 @@ function computeAutoformat(allNodes, allEdges, nodeIds) {
14419
14654
  const col = i % cols;
14420
14655
  const row = Math.floor(i / cols);
14421
14656
  let cx = originX;
14422
- for (let c16 = 0; c16 < col; c16++) cx += colWidths[c16] + COL_GAP2;
14657
+ for (let c17 = 0; c17 < col; c17++) cx += colWidths[c17] + COL_GAP2;
14423
14658
  let cy = originY;
14424
14659
  for (let r = 0; r < row; r++) cy += rowHeights[r] + ROW_GAP2;
14425
14660
  posMap.set(targets[i].id, { x: cx, y: cy });
@@ -14494,16 +14729,16 @@ function findNearestClearPosition(idealX, idealY, blockW, blockH, obstacles) {
14494
14729
  let bestPos = null;
14495
14730
  let bestMaxDim = Infinity;
14496
14731
  let bestDist = Infinity;
14497
- for (const c16 of candidates) {
14498
- if (!overlapsAny(c16.x, c16.y, blockW, blockH, obstacles)) {
14499
- const totalW = Math.max(allMaxX, c16.x + blockW) - Math.min(allMinX, c16.x);
14500
- const totalH = Math.max(allMaxY, c16.y + blockH) - Math.min(allMinY, c16.y);
14732
+ for (const c17 of candidates) {
14733
+ if (!overlapsAny(c17.x, c17.y, blockW, blockH, obstacles)) {
14734
+ const totalW = Math.max(allMaxX, c17.x + blockW) - Math.min(allMinX, c17.x);
14735
+ const totalH = Math.max(allMaxY, c17.y + blockH) - Math.min(allMinY, c17.y);
14501
14736
  const maxDim = Math.max(totalW, totalH);
14502
- const dist = Math.abs(c16.x - idealX) + Math.abs(c16.y - idealY);
14737
+ const dist = Math.abs(c17.x - idealX) + Math.abs(c17.y - idealY);
14503
14738
  if (maxDim < bestMaxDim || maxDim === bestMaxDim && dist < bestDist) {
14504
14739
  bestMaxDim = maxDim;
14505
14740
  bestDist = dist;
14506
- bestPos = c16;
14741
+ bestPos = c17;
14507
14742
  }
14508
14743
  }
14509
14744
  }
@@ -18215,6 +18450,82 @@ Examples:
18215
18450
  );
18216
18451
  }
18217
18452
 
18453
+ // src/commands/asset.ts
18454
+ function registerAssetCommands(program2, getOutputOpts) {
18455
+ const asset = program2.command("asset").description(
18456
+ "Search and place the team's Files (DAM) library assets. Requires a Pro or Enterprise plan"
18457
+ );
18458
+ asset.command("search").description(
18459
+ "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"
18460
+ ).argument(
18461
+ "<query>",
18462
+ 'Search terms \u2014 a filename or a description of the content, e.g. "Product_Shot_05" or "black jacket shot"'
18463
+ ).option("--limit <n>", "Max results (1-5, default 5)", "5").option(
18464
+ "--file-type <type>",
18465
+ "Filter by media type: image, video, audio, pdf, or text"
18466
+ ).option(
18467
+ "--canvas-id <canvasId>",
18468
+ "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)"
18469
+ ).addHelpText(
18470
+ "after",
18471
+ `
18472
+ Returns: { query, total, results: [{ assetId, filename, displayName, fileType, contentType, previewUrl, assetUrl, durationInMs }] }.
18473
+ Use the returned assetId values with mel asset place to bring them onto a canvas.
18474
+
18475
+ Examples:
18476
+ $ mel asset search "black jacket shot"
18477
+ $ mel asset search "logo" --file-type image --limit 3`
18478
+ ).action(
18479
+ async (query, opts) => {
18480
+ const client = getClient();
18481
+ const result = await client.asset.search({
18482
+ query: {
18483
+ query,
18484
+ limit: Number(opts.limit),
18485
+ ...opts.fileType !== void 0 && {
18486
+ fileType: opts.fileType
18487
+ },
18488
+ ...opts.canvasId !== void 0 && {
18489
+ canvasId: opts.canvasId
18490
+ }
18491
+ }
18492
+ });
18493
+ if (result.status !== 200) {
18494
+ handleApiError(result.status, result.body);
18495
+ }
18496
+ writeJson(result.body, getOutputOpts());
18497
+ }
18498
+ );
18499
+ asset.command("place").description(
18500
+ "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"
18501
+ ).argument(
18502
+ "<canvasId>",
18503
+ "Canvas ID to place onto (from mel canvas create or mel canvas list)"
18504
+ ).argument(
18505
+ "<assetIds...>",
18506
+ "One or more asset IDs from mel asset search (max 10)"
18507
+ ).addHelpText(
18508
+ "after",
18509
+ `
18510
+ Returns: { placed: [{ assetId, nodeId, mediaType }] }.
18511
+ Wire the returned nodeIds into generations with mel edge create.
18512
+
18513
+ Examples:
18514
+ $ mel asset place <canvasId> <assetId>
18515
+ $ mel asset place <canvasId> <assetId1> <assetId2>`
18516
+ ).action(async (canvasId, assetIds) => {
18517
+ const client = getClient();
18518
+ const result = await client.asset.place({
18519
+ params: { canvasId },
18520
+ body: { assetIds }
18521
+ });
18522
+ if (result.status !== 200) {
18523
+ handleApiError(result.status, result.body);
18524
+ }
18525
+ writeJson(result.body, getOutputOpts());
18526
+ });
18527
+ }
18528
+
18218
18529
  // src/commands/completion.ts
18219
18530
  var GLOBAL_FLAGS = [
18220
18531
  "--json",
@@ -18350,7 +18661,7 @@ function withDefaultHelp(args) {
18350
18661
  }
18351
18662
  function createProgram() {
18352
18663
  const program2 = new Command();
18353
- program2.name("mel").description("Mel \u2014 Melius agent CLI").version("0.3.0").showSuggestionAfterError(true).option("--json", "Force JSON output (default)").option("--text", "Human-readable output").option(
18664
+ program2.name("mel").description("Mel \u2014 Melius agent CLI").version("0.5.0").showSuggestionAfterError(true).option("--json", "Force JSON output (default)").option("--text", "Human-readable output").option(
18354
18665
  "--fields <fields>",
18355
18666
  "Select specific output fields (comma-separated)"
18356
18667
  ).option("--quiet", "Suppress output, exit code only").option("--verbose", "Log request/response metadata to stderr").addHelpText(
@@ -18394,6 +18705,7 @@ Learn more: https://docs.melius.com`
18394
18705
  registerSkillCommands(program2, getOutputOpts);
18395
18706
  }
18396
18707
  registerUploadCommands(program2, getOutputOpts);
18708
+ registerAssetCommands(program2, getOutputOpts);
18397
18709
  registerCompletionCommands(program2);
18398
18710
  applyStructuredErrors(program2);
18399
18711
  return program2;
@@ -18424,6 +18736,7 @@ export {
18424
18736
  EXIT_API_ERROR,
18425
18737
  formatError,
18426
18738
  writeError,
18739
+ printUpgradeNoticeIfNeeded,
18427
18740
  withDefaultHelp,
18428
18741
  createProgram
18429
18742
  };
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-JVD4O5T3.js";
5
+ } from "../chunk-UAK5HMFU.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.3.0",
3
+ "version": "0.5.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",