@melius-ai/cli 0.9.0 → 0.10.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
@@ -203,6 +203,31 @@ mel preset apply <canvasId> <presetId>
203
203
  mel bulk-run start <canvasId> --wait
204
204
  ```
205
205
 
206
+ ### Templates
207
+
208
+ Templates are reusable workflows applied server-side as collapsed boxes. Listing includes global templates and templates scoped to the current team. The CLI exposes only template metadata, declared ports, and the applied box IDs; internal workflow nodes and configuration stay private.
209
+
210
+ | Command | Description |
211
+ | -------------------------------------------- | ------------------------------------------------------------------------ |
212
+ | `mel template list` | List available templates and their declared input/output ports |
213
+ | `mel template apply <canvasId> <templateId>` | Apply a template as one collapsed box and return `instanceId`, `groupId` |
214
+
215
+ ```bash
216
+ # Discover templates and ports
217
+ mel template list
218
+ mel template list --fields id,title,inputPorts,outputPorts
219
+
220
+ # Apply with automatic placement
221
+ mel template apply <canvasId> <templateId>
222
+
223
+ # Materialize a text input while applying
224
+ mel template apply <canvasId> <templateId> \
225
+ --inputs '[{"kind":"text","portId":"<portId>","text":"A red shoe"}]'
226
+
227
+ # Run only the opaque template box
228
+ mel bulk-run start <canvasId> --node-ids <groupId> --wait
229
+ ```
230
+
206
231
  ### Models
207
232
 
208
233
  | Command | Description |
package/dist/bin/mel.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  printUpgradeNoticeIfNeeded,
8
8
  withDefaultHelp,
9
9
  writeError
10
- } from "../chunk-24FW76XZ.js";
10
+ } from "../chunk-GEI5UAWV.js";
11
11
 
12
12
  // bin/mel.ts
13
13
  var program = createProgram();
@@ -8647,12 +8647,17 @@ var AgentMessageFeedbackResponseSchema = external_exports.object({
8647
8647
  createdAt: external_exports.string().datetime()
8648
8648
  });
8649
8649
  var AgentSessionStatusSchema = external_exports.enum(["active", "ended", "error"]);
8650
+ var AgentSessionSourceSchema = external_exports.enum(["web", "slack"]);
8650
8651
  var AgentSessionResponseSchema = external_exports.object({
8651
8652
  id: external_exports.string().uuid(),
8652
8653
  userId: external_exports.string().uuid(),
8653
8654
  teamId: external_exports.string().uuid(),
8654
8655
  title: external_exports.string(),
8655
8656
  status: AgentSessionStatusSchema,
8657
+ /** Optional so web clients remain compatible with servers that predate session sources. */
8658
+ source: AgentSessionSourceSchema.optional(),
8659
+ /** True when this transcript is view-only on the current surface. */
8660
+ readOnly: external_exports.boolean().optional(),
8656
8661
  /** True when the agent is actively generating a response. */
8657
8662
  isGenerating: external_exports.boolean(),
8658
8663
  /** Present when the session is waiting for the user to approve a tool call. */
@@ -8781,6 +8786,28 @@ var agentContract = c3.router({
8781
8786
  },
8782
8787
  summary: "Get an agent session with recent messages"
8783
8788
  },
8789
+ getInternalDemoCanvasSession: {
8790
+ method: "GET",
8791
+ path: "/agent/internal-demo-canvas-sessions/:sessionId",
8792
+ pathParams: external_exports.object({ sessionId: external_exports.string().uuid() }),
8793
+ query: external_exports.object({
8794
+ messageLimit: external_exports.coerce.number().int().min(0).max(100).default(50)
8795
+ }),
8796
+ responses: {
8797
+ 200: AgentSessionResponseSchema
8798
+ },
8799
+ summary: "Get an internal demo canvas Slack transcript"
8800
+ },
8801
+ forkInternalDemoCanvasSession: {
8802
+ method: "POST",
8803
+ path: "/agent/internal-demo-canvas-sessions/:sessionId/fork",
8804
+ pathParams: external_exports.object({ sessionId: external_exports.string().uuid() }),
8805
+ body: null,
8806
+ responses: {
8807
+ 201: AgentSessionResponseSchema
8808
+ },
8809
+ summary: "Fork an internal demo canvas Slack transcript into a private agent session"
8810
+ },
8784
8811
  renameSession: {
8785
8812
  method: "PATCH",
8786
8813
  path: "/agent/sessions/:sessionId",
@@ -9797,6 +9824,20 @@ function parseCustomResolution(resolution) {
9797
9824
  return { width, height };
9798
9825
  }
9799
9826
  var MENTION_RE = /@\[([^\]]+)\](?:\{([^}]+)\})?/g;
9827
+ var MENTION_WITH_ID_RE = /@\[[^\]]+\]\{([^}]+)\}/g;
9828
+ function extractMentionedNodeIds(text) {
9829
+ if (!text) return [];
9830
+ const ids = [];
9831
+ const seen = /* @__PURE__ */ new Set();
9832
+ for (const match of text.matchAll(MENTION_WITH_ID_RE)) {
9833
+ const id = match[1];
9834
+ if (!seen.has(id)) {
9835
+ seen.add(id);
9836
+ ids.push(id);
9837
+ }
9838
+ }
9839
+ return ids;
9840
+ }
9800
9841
  var MAGIC_RESIZE_NODE_WIDTH = 500;
9801
9842
  var MAGIC_RESIZE_PREGEN_HEIGHT = 550;
9802
9843
 
@@ -10012,6 +10053,19 @@ var ElevenLabsVoiceSettingsSchema = external_exports.object({
10012
10053
  outputFormat: ElevenLabsOutputFormatSchema.optional()
10013
10054
  });
10014
10055
 
10056
+ // ../api-contract/src/generation-failure-reason.ts
10057
+ var GENERATION_FAILURE_REASONS = [
10058
+ "insufficient_credits",
10059
+ "content_policy",
10060
+ "no_media_generated",
10061
+ "provider_error",
10062
+ "validation_error",
10063
+ "timeout",
10064
+ "system",
10065
+ "unknown"
10066
+ ];
10067
+ var GenerationFailureReasonSchema = external_exports.enum(GENERATION_FAILURE_REASONS);
10068
+
10015
10069
  // ../api-contract/src/lora.ts
10016
10070
  var c6 = initContract();
10017
10071
  var LORA_UPLOAD_PART_SIZE_BYTES = 16 * 1024 * 1024;
@@ -10194,7 +10248,21 @@ var PreviewAssetSchema = external_exports.object({
10194
10248
  thumbnailUrl: external_exports.string().nullable()
10195
10249
  });
10196
10250
 
10251
+ // ../api-contract/src/template-port-summary.ts
10252
+ var TemplatePortSummarySchema = external_exports.object({
10253
+ id: external_exports.string(),
10254
+ direction: external_exports.enum(["input", "output"]),
10255
+ label: external_exports.string(),
10256
+ handleType: external_exports.string(),
10257
+ required: external_exports.boolean().optional(),
10258
+ maxConnections: external_exports.number().int().positive().optional(),
10259
+ outputAspectRatio: external_exports.enum(ASPECT_RATIOS).nullable().optional(),
10260
+ exampleAssetUrl: external_exports.string().nullable().optional(),
10261
+ exampleText: external_exports.string().nullable().optional()
10262
+ });
10263
+
10197
10264
  // ../api-contract/src/templates-ports.ts
10265
+ var TEMPLATE_EXAMPLE_TEXT_MAX_LENGTH = 2e3;
10198
10266
  var TemplatePortBaseFields = {
10199
10267
  /** Stable handle id on the box, e.g. "in_character", "out_angle_1". */
10200
10268
  id: external_exports.string(),
@@ -10211,7 +10279,25 @@ var TemplatePortBaseFields = {
10211
10279
  */
10212
10280
  internalHandle: external_exports.string().nullable(),
10213
10281
  /** Optional vertical position override on the box (e.g. "35%"). */
10214
- top: external_exports.string().optional()
10282
+ top: external_exports.string().optional(),
10283
+ /**
10284
+ * Example media for a MEDIA port (image/video/audio input source or output
10285
+ * result), shown in the template details panel and the build/edit authoring
10286
+ * UIs. `exampleAssetKey` is the persisted ref: an S3 object key under
10287
+ * `template-examples/`, copied at capture so the example outlives the source
10288
+ * canvas — exactly like `previewImageUrl`. `exampleAssetUrl` is the
10289
+ * freshly-signed display URL, populated on read only and ignored on write.
10290
+ * Both absent when the port has no media example (unset, or a text port).
10291
+ */
10292
+ exampleAssetKey: external_exports.string().nullable().optional(),
10293
+ exampleAssetUrl: external_exports.string().nullable().optional(),
10294
+ /**
10295
+ * Example prompt for a TEXT port — the text parallel of `exampleAssetKey`.
10296
+ * Stored inline (no S3, no signing) since it's just a string; the details
10297
+ * panel shows it truncated, and "use example as input" drops a text node
10298
+ * pre-filled with it. Absent for media ports and text ports with no example.
10299
+ */
10300
+ exampleText: external_exports.string().max(TEMPLATE_EXAMPLE_TEXT_MAX_LENGTH).nullable().optional()
10215
10301
  };
10216
10302
  var TemplatePortTargetSchema = external_exports.object({
10217
10303
  nodeKey: external_exports.string(),
@@ -10309,15 +10395,6 @@ var InstancePortBindingSchema = external_exports.discriminatedUnion("direction",
10309
10395
  ...InstancePortBindingBaseFields
10310
10396
  })
10311
10397
  ]);
10312
- var TemplatePortSummarySchema = external_exports.object({
10313
- id: external_exports.string(),
10314
- direction: external_exports.enum(["input", "output"]),
10315
- label: external_exports.string(),
10316
- handleType: external_exports.string(),
10317
- required: external_exports.boolean().optional(),
10318
- maxConnections: external_exports.number().int().positive().optional(),
10319
- outputAspectRatio: external_exports.enum(ASPECT_RATIOS).nullable().optional()
10320
- });
10321
10398
 
10322
10399
  // ../api-contract/src/canvas.ts
10323
10400
  var c7 = initContract();
@@ -10897,6 +10974,7 @@ var NodeRunResponseSchema = external_exports.object({
10897
10974
  errorMessage: external_exports.string().nullable(),
10898
10975
  errorSeverity: ErrorSeveritySchema.nullable().default(null),
10899
10976
  errorRetryable: external_exports.boolean().nullable().optional(),
10977
+ errorReason: GenerationFailureReasonSchema.nullable().optional(),
10900
10978
  numVariationsRequested: external_exports.number().int().nullable(),
10901
10979
  numVariationsCompleted: external_exports.number().int().nullable().optional(),
10902
10980
  numVariationsFailed: external_exports.number().int().nullable().optional(),
@@ -10951,6 +11029,7 @@ var LatestNodeRunResponseSchema = external_exports.object({
10951
11029
  errorMessage: external_exports.string().nullable(),
10952
11030
  errorSeverity: ErrorSeveritySchema.nullable().default(null),
10953
11031
  errorRetryable: external_exports.boolean().nullable().optional(),
11032
+ errorReason: GenerationFailureReasonSchema.nullable().optional(),
10954
11033
  numVariationsRequested: external_exports.number().int().nullable(),
10955
11034
  numVariationsCompleted: external_exports.number().int().nullable().optional(),
10956
11035
  numVariationsFailed: external_exports.number().int().nullable().optional(),
@@ -11257,7 +11336,33 @@ var CanvasSettingsResponseSchema = external_exports.object({
11257
11336
  createdAt: external_exports.string().datetime(),
11258
11337
  updatedAt: external_exports.string().datetime()
11259
11338
  });
11339
+ var PlaygroundCanvasReferenceSchema = external_exports.object({
11340
+ projectId: external_exports.string().uuid(),
11341
+ canvasId: external_exports.string().uuid()
11342
+ });
11343
+ var PlaygroundCanvasPageSchema = external_exports.object({
11344
+ projectId: external_exports.string().uuid().nullable(),
11345
+ data: external_exports.array(external_exports.object({ id: external_exports.string().uuid() })),
11346
+ total: external_exports.number().int().nonnegative()
11347
+ });
11260
11348
  var canvasContract = c7.router({
11349
+ createPlaygroundCanvas: {
11350
+ method: "POST",
11351
+ path: "/playground/canvases",
11352
+ body: null,
11353
+ responses: {
11354
+ 201: PlaygroundCanvasReferenceSchema,
11355
+ 409: MessageResponseSchema
11356
+ }
11357
+ },
11358
+ listPlaygroundCanvases: {
11359
+ method: "GET",
11360
+ path: "/playground/canvases",
11361
+ query: external_exports.object({
11362
+ ...paginationQuery({ maxLimit: 100, defaultLimit: 25 })
11363
+ }),
11364
+ responses: { 200: PlaygroundCanvasPageSchema }
11365
+ },
11261
11366
  createCanvas: {
11262
11367
  method: "POST",
11263
11368
  path: "/projects/:projectId/canvases",
@@ -13787,8 +13892,81 @@ var projectContract = c14.router({
13787
13892
  }
13788
13893
  });
13789
13894
 
13895
+ // ../api-contract/src/template-apply-input.ts
13896
+ var TemplateApplyInputSchema = external_exports.discriminatedUnion("kind", [
13897
+ external_exports.object({
13898
+ kind: external_exports.literal("asset"),
13899
+ portId: external_exports.string(),
13900
+ assetId: external_exports.string().uuid(),
13901
+ mediaType: external_exports.enum(["image", "video", "audio", "pdf"])
13902
+ }),
13903
+ external_exports.object({
13904
+ kind: external_exports.literal("text"),
13905
+ portId: external_exports.string(),
13906
+ text: external_exports.string().min(1)
13907
+ })
13908
+ ]);
13909
+
13910
+ // ../api-contract/src/template-public-api.ts
13911
+ var TemplateResponseSchema = external_exports.object({
13912
+ id: external_exports.string(),
13913
+ title: external_exports.string(),
13914
+ groupLabel: external_exports.string(),
13915
+ icon: external_exports.string(),
13916
+ description: external_exports.string(),
13917
+ previewImageUrl: external_exports.string().nullable(),
13918
+ previewVideoUrl: external_exports.string().nullable().optional(),
13919
+ inputPorts: external_exports.array(TemplatePortSummarySchema),
13920
+ outputPorts: external_exports.array(TemplatePortSummarySchema),
13921
+ canEdit: external_exports.boolean(),
13922
+ isGlobal: external_exports.boolean().optional(),
13923
+ authorName: external_exports.string().nullable().optional(),
13924
+ estimatedCreditCost: external_exports.number().optional(),
13925
+ tags: external_exports.array(external_exports.string()).optional()
13926
+ });
13927
+ var PublicApplyTemplateResponseSchema = external_exports.object({
13928
+ /** The created template instance id. */
13929
+ instanceId: external_exports.string(),
13930
+ /** The collapsed group id shown on the canvas. */
13931
+ groupId: external_exports.string()
13932
+ });
13933
+ var ApplyTemplateBodySchema = external_exports.object({
13934
+ canvasId: external_exports.string().uuid(),
13935
+ templateId: external_exports.string().uuid(),
13936
+ /** Box center X. Auto-positioned to avoid existing nodes if omitted. */
13937
+ centerX: external_exports.number().optional(),
13938
+ /** Box center Y. Auto-positioned to avoid existing nodes if omitted. */
13939
+ centerY: external_exports.number().optional(),
13940
+ /** Optional input values materialized with the template instance. */
13941
+ inputs: external_exports.array(TemplateApplyInputSchema).optional()
13942
+ });
13943
+ var templateListRoute = {
13944
+ method: "GET",
13945
+ path: "/templates",
13946
+ responses: {
13947
+ 200: external_exports.object({
13948
+ data: external_exports.array(TemplateResponseSchema)
13949
+ })
13950
+ },
13951
+ summary: "List available templates (active)"
13952
+ };
13953
+ var templateApplyRouteBase = {
13954
+ method: "POST",
13955
+ path: "/templates/apply",
13956
+ body: ApplyTemplateBodySchema,
13957
+ summary: "Apply a template as a collapsed canvas box"
13958
+ };
13959
+ var publicTemplateApplyRoute = {
13960
+ ...templateApplyRouteBase,
13961
+ responses: {
13962
+ 200: PublicApplyTemplateResponseSchema,
13963
+ 404: MessageResponseSchema
13964
+ }
13965
+ };
13966
+
13790
13967
  // ../api-contract/src/upload.ts
13791
13968
  var c15 = initContract();
13969
+ var PdfExtractionModeSchema = external_exports.enum(["summary", "pages"]);
13792
13970
  var InitUploadBodySchema = external_exports.object({
13793
13971
  nodeId: external_exports.string().uuid().optional(),
13794
13972
  filename: external_exports.string(),
@@ -13848,7 +14026,10 @@ var CompleteUploadBodySchema = external_exports.object({
13848
14026
  // edit/upstream change to keep `src` fresh for downstream nodes; those
13849
14027
  // background refreshes set this so they don't mint a new version each time.
13850
14028
  // Explicit user saves omit it and fork a new version as usual.
13851
- replaceActiveVersion: external_exports.boolean().optional()
14029
+ replaceActiveVersion: external_exports.boolean().optional(),
14030
+ // PDF-only: how to process the upload once complete. Omitted ⇒ "summary"
14031
+ // (the server default and every non-PDF upload). See PdfExtractionModeSchema.
14032
+ pdfExtractionMode: PdfExtractionModeSchema.optional()
13852
14033
  });
13853
14034
  var CompleteUploadResponseSchema = external_exports.object({
13854
14035
  success: external_exports.boolean(),
@@ -13969,6 +14150,10 @@ var publicContract = c16.router(
13969
14150
  preset: {
13970
14151
  list: presetsContract.listPresets
13971
14152
  },
14153
+ template: {
14154
+ list: templateListRoute,
14155
+ apply: publicTemplateApplyRoute
14156
+ },
13972
14157
  team: {
13973
14158
  list: teamContract.listMyTeams
13974
14159
  },
@@ -14220,7 +14405,7 @@ function isMajorUpgrade(current, latest) {
14220
14405
  return l[0] > c17[0];
14221
14406
  }
14222
14407
  function shouldShowUpgradeNotice(ctx = {}) {
14223
- const current = ctx.current ?? "0.9.0";
14408
+ const current = ctx.current ?? "0.10.0";
14224
14409
  const latest = ctx.latest ?? serverLatestVersion;
14225
14410
  const env = ctx.env ?? process.env;
14226
14411
  const isTty = ctx.isTty ?? Boolean(process.stderr.isTTY);
@@ -14241,7 +14426,7 @@ Upgrade: npm install -g @melius-ai/cli@latest (or: brew update && brew upgrade
14241
14426
  function printUpgradeNoticeIfNeeded(ctx = {}) {
14242
14427
  try {
14243
14428
  if (!shouldShowUpgradeNotice(ctx)) return;
14244
- const current = ctx.current ?? "0.9.0";
14429
+ const current = ctx.current ?? "0.10.0";
14245
14430
  const latest = ctx.latest ?? serverLatestVersion;
14246
14431
  process.stderr.write(formatUpgradeNotice(current, latest));
14247
14432
  } catch {
@@ -15273,14 +15458,27 @@ function shiftToAvoidExisting(result, allNodes, targetIds, allEdges = []) {
15273
15458
  }));
15274
15459
  const existingIds = new Set(otherNodes.map((n) => n.id));
15275
15460
  const nodeById = new Map(allNodes.map((n) => [n.id, n]));
15461
+ function resolveTopLevelNodeId(id) {
15462
+ let resolvedId = id;
15463
+ const seen = /* @__PURE__ */ new Set();
15464
+ while (!seen.has(resolvedId)) {
15465
+ seen.add(resolvedId);
15466
+ const parentId2 = nodeById.get(resolvedId)?.groupId;
15467
+ if (!parentId2) break;
15468
+ resolvedId = parentId2;
15469
+ }
15470
+ return resolvedId;
15471
+ }
15276
15472
  const incomingSourceIds = /* @__PURE__ */ new Set();
15277
15473
  const outgoingTargetIds = /* @__PURE__ */ new Set();
15278
15474
  for (const edge of allEdges) {
15279
- if (existingIds.has(edge.srcNodeId) && targetIds.has(edge.dstNodeId)) {
15280
- incomingSourceIds.add(edge.srcNodeId);
15475
+ const srcNodeId = resolveTopLevelNodeId(edge.srcNodeId);
15476
+ const dstNodeId = resolveTopLevelNodeId(edge.dstNodeId);
15477
+ if (existingIds.has(srcNodeId) && targetIds.has(dstNodeId)) {
15478
+ incomingSourceIds.add(srcNodeId);
15281
15479
  }
15282
- if (targetIds.has(edge.srcNodeId) && existingIds.has(edge.dstNodeId)) {
15283
- outgoingTargetIds.add(edge.dstNodeId);
15480
+ if (targetIds.has(srcNodeId) && existingIds.has(dstNodeId)) {
15481
+ outgoingTargetIds.add(dstNodeId);
15284
15482
  }
15285
15483
  }
15286
15484
  const SCATTER_THRESHOLD = 3;
@@ -15498,11 +15696,36 @@ function planLayout(content, planned, edges, anchorNodeIds) {
15498
15696
  srcNodeId: e.srcNodeId,
15499
15697
  dstNodeId: e.dstNodeId
15500
15698
  }));
15699
+ const canvasNodeIds = new Set(content.nodes.map((n) => n.id));
15700
+ const mentionEdges = [];
15701
+ let mentionEdgeIndex = 0;
15702
+ for (const p of planned) {
15703
+ const mentionIds = /* @__PURE__ */ new Set([
15704
+ ...extractMentionedNodeIds(p.prompt),
15705
+ ...extractMentionedNodeIds(p.text)
15706
+ ]);
15707
+ for (const mentionedId of mentionIds) {
15708
+ let srcId;
15709
+ if (virtualIds.has(mentionedId)) {
15710
+ srcId = mentionedId;
15711
+ } else if (canvasNodeIds.has(mentionedId)) {
15712
+ const node = nodeById.get(mentionedId);
15713
+ srcId = node?.groupId ?? mentionedId;
15714
+ }
15715
+ if (!srcId || srcId === p.id) continue;
15716
+ mentionEdges.push({
15717
+ id: `mention-${mentionEdgeIndex++}`,
15718
+ srcNodeId: srcId,
15719
+ dstNodeId: p.id
15720
+ });
15721
+ }
15722
+ }
15501
15723
  const anchorEdges = [];
15502
15724
  if (anchorNodes && anchorNodes.length > 0) {
15503
15725
  const hasExplicitAnchorEdge = [
15504
15726
  ...plannedEdges,
15505
- ...relevantExisting
15727
+ ...relevantExisting,
15728
+ ...mentionEdges
15506
15729
  ].some(
15507
15730
  (e) => anchorNodeIds.includes(e.srcNodeId) && virtualIds.has(e.dstNodeId)
15508
15731
  );
@@ -15518,7 +15741,17 @@ function planLayout(content, planned, edges, anchorNodeIds) {
15518
15741
  }
15519
15742
  }
15520
15743
  }
15521
- const allEdges = [...plannedEdges, ...relevantExisting, ...anchorEdges];
15744
+ const baseEdges = [...plannedEdges, ...relevantExisting, ...anchorEdges];
15745
+ const baseEdgeKeys = new Set(
15746
+ baseEdges.map((e) => `${e.srcNodeId}->${e.dstNodeId}`)
15747
+ );
15748
+ const uniqueMentionEdges = mentionEdges.filter((e) => {
15749
+ const key = `${e.srcNodeId}->${e.dstNodeId}`;
15750
+ if (baseEdgeKeys.has(key)) return false;
15751
+ baseEdgeKeys.add(key);
15752
+ return true;
15753
+ });
15754
+ const allEdges = [...baseEdges, ...uniqueMentionEdges];
15522
15755
  const allNodes = [...existingNodes, ...virtualNodes];
15523
15756
  const virtualIdsArr = [...virtualIds];
15524
15757
  const rawResult = computeAutoformat(allNodes, allEdges, virtualIdsArr);
@@ -16733,7 +16966,9 @@ Valid node types: text, image, video, audio, file, group, custom_text, stitch.
16733
16966
  const planned = itemsWithoutGeometry.map((item) => ({
16734
16967
  id: item.id ?? item.type,
16735
16968
  nodeType: item.type,
16736
- aspectRatio: item.aspectRatio
16969
+ aspectRatio: item.aspectRatio,
16970
+ prompt: item.prompt,
16971
+ text: item.text
16737
16972
  }));
16738
16973
  const plannedIds = new Set(planned.map((p) => p.id));
16739
16974
  const edges = (edgeItems ?? []).filter(
@@ -18750,6 +18985,120 @@ Examples:
18750
18985
  );
18751
18986
  }
18752
18987
 
18988
+ // src/commands/template.ts
18989
+ function parseCoordinate(value, option) {
18990
+ if (value === void 0) return void 0;
18991
+ const parsed = Number(value);
18992
+ if (Number.isFinite(parsed)) return parsed;
18993
+ writeError(
18994
+ formatError("USAGE_ERROR", `${option} must be a finite number`, {
18995
+ suggestion: `Provide ${option} <number> or omit it for automatic placement`
18996
+ }),
18997
+ EXIT_USAGE_ERROR
18998
+ );
18999
+ }
19000
+ function parseInputs(value) {
19001
+ if (value === void 0) return void 0;
19002
+ let parsed;
19003
+ try {
19004
+ parsed = JSON.parse(value);
19005
+ } catch {
19006
+ writeError(
19007
+ formatError("USAGE_ERROR", "Invalid JSON in --inputs", {
19008
+ suggestion: `--inputs must be a JSON array, for example '[{"kind":"text","portId":"prompt","text":"A red shoe"}]'`
19009
+ }),
19010
+ EXIT_USAGE_ERROR
19011
+ );
19012
+ }
19013
+ const result = TemplateApplyInputSchema.array().safeParse(parsed);
19014
+ if (result.success) return result.data;
19015
+ writeError(
19016
+ formatError(
19017
+ "USAGE_ERROR",
19018
+ result.error.issues.map((issue) => issue.message).join("; "),
19019
+ {
19020
+ suggestion: 'Use text inputs {"kind":"text","portId":"<portId>","text":"<text>"} or asset inputs {"kind":"asset","portId":"<portId>","assetId":"<assetId>","mediaType":"image|video|audio|pdf"}'
19021
+ }
19022
+ ),
19023
+ EXIT_USAGE_ERROR
19024
+ );
19025
+ }
19026
+ function registerTemplateCommands(program2, getOutputOpts) {
19027
+ const template = program2.command("template").description("List and apply opaque canvas workflow templates");
19028
+ template.command("list").description(
19029
+ "List available templates with display metadata and declared input/output ports"
19030
+ ).addHelpText(
19031
+ "after",
19032
+ `
19033
+ Returns: Array of templates with id, title, description, previewImageUrl, inputPorts, outputPorts, and estimatedCreditCost.
19034
+ Includes global templates and templates scoped to the current team.
19035
+ The template's internal workflow stays private. Use a returned id with mel template apply.
19036
+
19037
+ Examples:
19038
+ $ mel template list
19039
+ $ mel template list --fields id,title,inputPorts,outputPorts`
19040
+ ).action(async () => {
19041
+ const client = getClient();
19042
+ const result = await client.template.list();
19043
+ if (result.status !== 200) {
19044
+ handleApiError(result.status, result.body);
19045
+ }
19046
+ writeJson(result.body.data, getOutputOpts());
19047
+ });
19048
+ template.command("apply").description(
19049
+ "Apply a template to a canvas as one collapsed box and return its instanceId and groupId"
19050
+ ).argument(
19051
+ "<canvasId>",
19052
+ "Canvas ID (from mel canvas create or mel canvas list output)"
19053
+ ).argument("<templateId>", "Template ID (from mel template list output)").option(
19054
+ "--center-x <number>",
19055
+ "X center for the template box. Omit for automatic non-overlapping placement"
19056
+ ).option(
19057
+ "--center-y <number>",
19058
+ "Y center for the template box. Omit for automatic non-overlapping placement"
19059
+ ).option(
19060
+ "--inputs <json>",
19061
+ "JSON array of text or library-asset inputs targeting port IDs from mel template list"
19062
+ ).addHelpText(
19063
+ "after",
19064
+ `
19065
+ Returns: { instanceId, groupId }. Internal workflow node IDs are never returned.
19066
+ Input kinds: text {kind,portId,text}; asset {kind,portId,assetId,mediaType} where mediaType is image, video, audio, or pdf.
19067
+ Omit --center-x/--center-y to place the box automatically. Use mel canvas content <canvasId> afterward to inspect its public ports.
19068
+ Run the applied template with mel bulk-run start <canvasId> --node-ids <groupId> --wait.
19069
+
19070
+ Examples:
19071
+ $ mel template apply <canvasId> <templateId>
19072
+ $ mel template apply <canvasId> <templateId> --inputs '[{"kind":"text","portId":"<portId>","text":"A red shoe"}]'
19073
+ $ mel bulk-run start <canvasId> --node-ids <groupId> --wait`
19074
+ ).action(
19075
+ async (canvasId, templateId, opts) => {
19076
+ const client = getClient();
19077
+ const centerX = parseCoordinate(opts.centerX, "--center-x");
19078
+ const centerY = parseCoordinate(opts.centerY, "--center-y");
19079
+ const inputs = parseInputs(opts.inputs);
19080
+ const result = await client.template.apply({
19081
+ body: {
19082
+ canvasId,
19083
+ templateId,
19084
+ ...centerX !== void 0 && { centerX },
19085
+ ...centerY !== void 0 && { centerY },
19086
+ ...inputs !== void 0 && { inputs }
19087
+ }
19088
+ });
19089
+ if (result.status !== 200) {
19090
+ handleApiError(
19091
+ result.status,
19092
+ result.body,
19093
+ "Run: mel template list"
19094
+ );
19095
+ }
19096
+ const { instanceId, groupId } = result.body;
19097
+ writeJson({ instanceId, groupId }, getOutputOpts());
19098
+ }
19099
+ );
19100
+ }
19101
+
18753
19102
  // src/lib/upload.ts
18754
19103
  import * as fs3 from "fs";
18755
19104
  import * as path3 from "path";
@@ -19205,7 +19554,7 @@ function withDefaultHelp(args) {
19205
19554
  }
19206
19555
  function createProgram() {
19207
19556
  const program2 = new Command();
19208
- program2.name("mel").description("Mel \u2014 Melius agent CLI").version("0.9.0").showSuggestionAfterError(true).option("--json", "Force JSON output (default)").option("--text", "Human-readable output").option(
19557
+ program2.name("mel").description("Mel \u2014 Melius agent CLI").version("0.10.0").showSuggestionAfterError(true).option("--json", "Force JSON output (default)").option("--text", "Human-readable output").option(
19209
19558
  "--fields <fields>",
19210
19559
  "Select specific output fields (comma-separated)"
19211
19560
  ).option("--quiet", "Suppress output, exit code only").option("--verbose", "Log request/response metadata to stderr").addHelpText(
@@ -19244,6 +19593,7 @@ Learn more: https://docs.melius.com`
19244
19593
  registerAudioCommands(program2, getOutputOpts);
19245
19594
  registerFontCommands(program2, getOutputOpts);
19246
19595
  registerPresetCommands(program2, getOutputOpts);
19596
+ registerTemplateCommands(program2, getOutputOpts);
19247
19597
  registerConfigCommands(program2, getOutputOpts);
19248
19598
  if (false) {
19249
19599
  registerSkillCommands(program2, getOutputOpts);
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-24FW76XZ.js";
5
+ } from "../chunk-GEI5UAWV.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.9.0",
3
+ "version": "0.10.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",