@melius-ai/cli 0.2.1 → 0.3.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
@@ -8,7 +8,7 @@ The programmatic interface to the Melius platform. Built for AI agents running i
8
8
  npm install -g @melius-ai/cli
9
9
  # or with Homebrew (macOS/Linux): brew install melius-ai/tap/mel
10
10
 
11
- # Authenticate
11
+ # Authenticate (see "Get an API key" below to obtain a key)
12
12
  mel auth login --api-key mel_<your-key>
13
13
 
14
14
  # Build a canvas
@@ -19,6 +19,12 @@ mel run start <nodeId> --canvas-id <canvasId>
19
19
  mel run wait <runId>
20
20
  ```
21
21
 
22
+ ## Get an API key
23
+
24
+ 1. Sign up at [app.melius.com/signup](https://app.melius.com/signup) if you don't have a Melius account yet.
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
+ 3. Authenticate the CLI with it: `mel auth login --api-key mel_<your-key>`.
27
+
22
28
  ## Sandbox setup
23
29
 
24
30
  In the Claude Code sandbox, only two environment variables are needed:
@@ -323,7 +329,7 @@ All commands follow the same contract:
323
329
  "code": "UNAUTHORIZED",
324
330
  "message": "Invalid or expired API key",
325
331
  "status": 401,
326
- "suggestion": "Run: mel auth login --api-key <key>"
332
+ "suggestion": "Sign up at https://app.melius.com/signup, then create an API key in the Melius app under Team settings → Integrations. Then run: mel auth login --api-key mel_..."
327
333
  }
328
334
  }
329
335
  ```
package/dist/bin/mel.js CHANGED
@@ -4,13 +4,16 @@ import {
4
4
  EXIT_API_ERROR,
5
5
  createProgram,
6
6
  formatError,
7
+ withDefaultHelp,
7
8
  writeError
8
- } from "../chunk-6IN2VKUC.js";
9
+ } from "../chunk-JVD4O5T3.js";
9
10
 
10
11
  // bin/mel.ts
11
12
  var program = createProgram();
12
13
  try {
13
- await program.parseAsync(process.argv);
14
+ await program.parseAsync(withDefaultHelp(process.argv.slice(2)), {
15
+ from: "user"
16
+ });
14
17
  } catch (err) {
15
18
  writeError(
16
19
  formatError(
@@ -8112,6 +8112,18 @@ var BulkMoveFilesBodySchema = external_exports.object({
8112
8112
  assetIds: BulkFileAssetIdsSchema,
8113
8113
  folderId: external_exports.string().uuid().nullable()
8114
8114
  });
8115
+ var BulkDownloadableIdsSchema = external_exports.array(external_exports.string().uuid()).max(MAX_BULK_FILE_ACTION_ASSET_IDS).default([]);
8116
+ var BulkDownloadFilesBodySchema = external_exports.object({
8117
+ assetIds: BulkDownloadableIdsSchema,
8118
+ folderIds: BulkDownloadableIdsSchema
8119
+ }).refine((body) => body.assetIds.length + body.folderIds.length > 0, {
8120
+ message: "Select at least one file or folder to download"
8121
+ });
8122
+ var BulkDownloadFilesResponseSchema = external_exports.object({
8123
+ // Presigned URL of a freshly built zip archive of the requested assets.
8124
+ url: external_exports.string().url(),
8125
+ filename: external_exports.string()
8126
+ });
8115
8127
  var BulkHideFilesBodySchema = external_exports.object({
8116
8128
  assetIds: BulkFileAssetIdsSchema
8117
8129
  });
@@ -8218,6 +8230,10 @@ var CompleteFilesUploadBodySchema = external_exports.object({
8218
8230
  var CompleteFilesUploadResponseSchema = external_exports.object({
8219
8231
  file: FileCardSchema
8220
8232
  });
8233
+ var EmbeddingStorageUsageResponseSchema = external_exports.object({
8234
+ usedBytes: external_exports.number(),
8235
+ limitBytes: external_exports.number()
8236
+ });
8221
8237
  var ErrorBodySchema = MessageResponseSchema;
8222
8238
  var filesContract = c2.router({
8223
8239
  createFileFolder: {
@@ -8287,6 +8303,17 @@ var filesContract = c2.router({
8287
8303
  },
8288
8304
  summary: "Move a file to a folder"
8289
8305
  },
8306
+ bulkDownloadFiles: {
8307
+ method: "POST",
8308
+ path: "/files/bulk/download",
8309
+ body: BulkDownloadFilesBodySchema,
8310
+ responses: {
8311
+ 200: BulkDownloadFilesResponseSchema,
8312
+ 403: ErrorBodySchema,
8313
+ 404: ErrorBodySchema
8314
+ },
8315
+ summary: "Download files and folders as a zip archive"
8316
+ },
8290
8317
  bulkHideFiles: {
8291
8318
  method: "POST",
8292
8319
  path: "/files/bulk/hide",
@@ -8353,6 +8380,15 @@ var filesContract = c2.router({
8353
8380
  },
8354
8381
  summary: "Search team assets for the agent"
8355
8382
  },
8383
+ getEmbeddingStorageUsage: {
8384
+ method: "GET",
8385
+ path: "/files/embedding-storage-usage",
8386
+ responses: {
8387
+ 200: EmbeddingStorageUsageResponseSchema,
8388
+ 403: ErrorBodySchema
8389
+ },
8390
+ summary: "Get the team's DAM embedding-storage usage and cap"
8391
+ },
8356
8392
  getFileDetail: {
8357
8393
  method: "GET",
8358
8394
  path: "/files/:assetId/detail",
@@ -9052,7 +9088,7 @@ var AUTO_MODEL_REGISTRY = [
9052
9088
  {
9053
9089
  nodeType: "video",
9054
9090
  inputPattern: "text-only",
9055
- standard: { model: "ltx-2.3", variant: "text-to-video" },
9091
+ standard: { model: "kling-o3", variant: "text-to-video" },
9056
9092
  fast: { model: "grok-imagine-video", variant: "text-to-video" }
9057
9093
  },
9058
9094
  {
@@ -9070,7 +9106,7 @@ var AUTO_MODEL_REGISTRY = [
9070
9106
  {
9071
9107
  nodeType: "video",
9072
9108
  inputPattern: "reference-to-video",
9073
- standard: { model: "veo-3.1", variant: "reference-to-video" },
9109
+ standard: { model: "kling-o3", variant: "reference-to-video" },
9074
9110
  fast: { model: "kling-o3", variant: "reference-to-video" }
9075
9111
  },
9076
9112
  {
@@ -9115,13 +9151,13 @@ var AUTO_MODEL_REGISTRY = [
9115
9151
  {
9116
9152
  nodeType: "image",
9117
9153
  inputPattern: "text-to-image",
9118
- standard: { model: "gpt-image-2-medium", variant: "text-to-image" },
9154
+ standard: { model: "nano-banana-pro", variant: "text-to-image" },
9119
9155
  fast: { model: "flux-schnell", variant: "text-to-image" }
9120
9156
  },
9121
9157
  {
9122
9158
  nodeType: "image",
9123
9159
  inputPattern: "image-to-image",
9124
- standard: { model: "gpt-image-2-medium", variant: "image-to-image" },
9160
+ standard: { model: "nano-banana-pro", variant: "image-to-image" },
9125
9161
  fast: { model: "grok-imagine", variant: "image-to-image" }
9126
9162
  },
9127
9163
  // ── Text ────────────────────────────────────────────────────────────
@@ -10222,6 +10258,9 @@ var CanvasContentResponseSchema = external_exports.object({
10222
10258
  edges: external_exports.array(EdgeResponseSchema),
10223
10259
  modelPresets: CanvasMediaPresetSchema.optional().describe(
10224
10260
  "Canvas-wide default model + variant the user configured in canvas settings, per category (image/video/text/audio). When a node is created with no modelConfig, the server pins these defaults exactly. Use this to know what model+mode will apply, so you can pick correct edge handles and avoid hallucinating the default"
10261
+ ),
10262
+ videoDurationOptions: external_exports.array(external_exports.number().int().positive()).optional().describe(
10263
+ "The exact clip durations (seconds) the canvas default video model supports, e.g. [3,4,5,\u2026,15]. Pick each video node's duration from this list \u2014 it is the real range, so you never need model_list just to find valid durations, and you can use short values (like 3s) and odd values, not only round 5s/10s. A value below a specific variant's floor is clamped up automatically."
10225
10264
  )
10226
10265
  });
10227
10266
  var QueryBooleanSchema = external_exports.preprocess((value) => {
@@ -11319,6 +11358,35 @@ var canvasContract = c6.router({
11319
11358
  400: MessageResponseSchema
11320
11359
  }
11321
11360
  },
11361
+ validateBulkRun: {
11362
+ method: "POST",
11363
+ path: "/canvases/:canvasId/bulk-run/validate",
11364
+ pathParams: external_exports.object({ canvasId: external_exports.string().uuid() }),
11365
+ body: external_exports.object({
11366
+ nodeIds: external_exports.array(external_exports.string().uuid()).min(1).max(500),
11367
+ // Input nodes the author marked OPTIONAL (may be absent when the
11368
+ // template is applied). Preflight validates the worst case by
11369
+ // dropping edges from these sources, so a downstream node left
11370
+ // without its only required input (e.g. every prompt-feeder made
11371
+ // optional) fails here instead of only at apply time.
11372
+ optionalSourceNodeIds: external_exports.array(external_exports.string().uuid()).optional()
11373
+ }),
11374
+ responses: {
11375
+ // Read-only preflight: collect every reason a node (or the graph)
11376
+ // can't run instead of failing on the first. `nodeId: null` is a
11377
+ // graph-level issue (e.g. a cycle). Empty `issues` = runnable.
11378
+ 200: external_exports.object({
11379
+ issues: external_exports.array(
11380
+ external_exports.object({
11381
+ nodeId: external_exports.string().uuid().nullable(),
11382
+ message: external_exports.string()
11383
+ })
11384
+ )
11385
+ }),
11386
+ 400: MessageResponseSchema
11387
+ },
11388
+ summary: "Validate that a set of nodes can run as a bulk run, collecting all blockers"
11389
+ },
11322
11390
  getBulkNodeRun: {
11323
11391
  method: "GET",
11324
11392
  path: "/bulk-node-runs/:bulkNodeRunId",
@@ -12090,6 +12158,25 @@ var generationContract = c8.router({
12090
12158
  200: ListGenerationHistoryResponseSchema
12091
12159
  }
12092
12160
  },
12161
+ downloadGenerationHistory: {
12162
+ method: "GET",
12163
+ path: "/generation/history/download",
12164
+ // Same filters as the paginated list, minus pagination: an export
12165
+ // returns every matching row, not a page.
12166
+ query: external_exports.object({
12167
+ search: external_exports.string().optional(),
12168
+ userId: external_exports.string().optional(),
12169
+ projectId: external_exports.string().optional(),
12170
+ since: external_exports.string().datetime().optional(),
12171
+ until: external_exports.string().datetime().optional()
12172
+ }),
12173
+ responses: {
12174
+ 200: c8.otherResponse({
12175
+ contentType: "text/csv",
12176
+ body: external_exports.string()
12177
+ })
12178
+ }
12179
+ },
12093
12180
  getGenerationsByWeek: {
12094
12181
  method: "GET",
12095
12182
  path: "/generation/generations-by-week",
@@ -12586,7 +12673,25 @@ var InternalEnterpriseOrderSchema = EnterpriseOrderSchema.extend({
12586
12673
  });
12587
12674
  var TeamAdvancedSettingsSchema = external_exports.object({
12588
12675
  negativePromptEnabled: external_exports.boolean().optional(),
12589
- customSeedEnabled: external_exports.boolean().optional()
12676
+ customSeedEnabled: external_exports.boolean().optional(),
12677
+ // Auto-approve join requests from signups whose email domain matches this
12678
+ // enterprise team's owner domain. Admin-governed via the dedicated
12679
+ // domain-join-settings endpoint, not the editor-facing advanced-settings write.
12680
+ domainJoinAutoApprove: external_exports.boolean().optional()
12681
+ });
12682
+ var DomainJoinTargetSchema = external_exports.object({
12683
+ teamId: external_exports.string().uuid(),
12684
+ teamName: external_exports.string(),
12685
+ teamPhotoUrl: external_exports.string().nullable(),
12686
+ alreadyRequested: external_exports.boolean()
12687
+ });
12688
+ var JoinRequestSchema = external_exports.object({
12689
+ id: external_exports.string().uuid(),
12690
+ userId: external_exports.string().uuid(),
12691
+ userName: external_exports.string(),
12692
+ userEmail: external_exports.string(),
12693
+ avatarUrl: external_exports.string().nullable(),
12694
+ createdAt: external_exports.string().datetime()
12590
12695
  });
12591
12696
  var ActiveProductFeatureOverrideSchema = external_exports.object({
12592
12697
  feature: external_exports.string().min(1),
@@ -12661,6 +12766,23 @@ var teamContract = c12.router({
12661
12766
  body: external_exports.object({ token: external_exports.string().min(1) }),
12662
12767
  responses: { 200: TeamMembershipSchema }
12663
12768
  },
12769
+ getDomainJoinTarget: {
12770
+ method: "GET",
12771
+ path: "/teams/domain-join-target",
12772
+ responses: {
12773
+ 200: external_exports.object({ target: DomainJoinTargetSchema.nullable() })
12774
+ }
12775
+ },
12776
+ requestDomainJoin: {
12777
+ method: "POST",
12778
+ path: "/teams/domain-join-requests",
12779
+ body: external_exports.object({ teamId: external_exports.string().uuid() }),
12780
+ responses: {
12781
+ // "joined" when the team auto-approves; "requested" when it awaits
12782
+ // admin approval.
12783
+ 200: external_exports.object({ outcome: external_exports.enum(["joined", "requested"]) })
12784
+ }
12785
+ },
12664
12786
  // --- Team-scoped (requires X-Team-Id header) ---
12665
12787
  updateTeam: {
12666
12788
  method: "PATCH",
@@ -12747,6 +12869,42 @@ var teamContract = c12.router({
12747
12869
  pathParams: external_exports.object({ inviteId: external_exports.string().uuid() }),
12748
12870
  body: null,
12749
12871
  responses: { 204: c12.noBody() }
12872
+ },
12873
+ listJoinRequests: {
12874
+ method: "GET",
12875
+ path: "/teams/current/join-requests",
12876
+ responses: { 200: external_exports.array(JoinRequestSchema) }
12877
+ },
12878
+ approveJoinRequest: {
12879
+ method: "POST",
12880
+ path: "/teams/current/join-requests/:requestId/approve",
12881
+ pathParams: external_exports.object({ requestId: external_exports.string().uuid() }),
12882
+ body: external_exports.object({ role: InviteLinkRoleSchema }),
12883
+ responses: { 200: MemberSchema }
12884
+ },
12885
+ denyJoinRequest: {
12886
+ method: "DELETE",
12887
+ path: "/teams/current/join-requests/:requestId",
12888
+ pathParams: external_exports.object({ requestId: external_exports.string().uuid() }),
12889
+ body: null,
12890
+ responses: { 204: c12.noBody() }
12891
+ },
12892
+ updateDomainJoinSettings: {
12893
+ method: "PATCH",
12894
+ path: "/teams/current/domain-join-settings",
12895
+ body: external_exports.object({ autoApprove: external_exports.boolean() }),
12896
+ responses: {
12897
+ 200: external_exports.object({ advancedSettings: TeamAdvancedSettingsSchema })
12898
+ }
12899
+ },
12900
+ // Admin directly adds a signup on the team's email domain — no invite for
12901
+ // the user to accept. Only users whose email domain routes to this team can
12902
+ // be added this way.
12903
+ addDomainMember: {
12904
+ method: "POST",
12905
+ path: "/teams/current/domain-members",
12906
+ body: external_exports.object({ email: external_exports.string().email() }),
12907
+ responses: { 200: MemberSchema }
12750
12908
  }
12751
12909
  });
12752
12910
 
@@ -13191,6 +13349,10 @@ var publicContract = c15.router(
13191
13349
  }
13192
13350
  );
13193
13351
 
13352
+ // src/lib/onboarding.ts
13353
+ var SIGNUP_URL = "https://app.melius.com/signup";
13354
+ var GET_API_KEY_STEPS = `Sign up at ${SIGNUP_URL}, then create an API key in the Melius app under Team settings \u2192 Integrations`;
13355
+
13194
13356
  // src/lib/errors.ts
13195
13357
  var EXIT_API_ERROR = 1;
13196
13358
  var EXIT_USAGE_ERROR = 2;
@@ -13216,7 +13378,7 @@ function handleApiError(status, body, suggestion) {
13216
13378
  writeError(
13217
13379
  formatError("UNAUTHORIZED", "Invalid or expired API key", {
13218
13380
  status,
13219
- suggestion: suggestion ?? "Run: mel auth login --api-key <key>"
13381
+ suggestion: suggestion ?? `${GET_API_KEY_STEPS}. Then run: mel auth login --api-key mel_...`
13220
13382
  }),
13221
13383
  EXIT_AUTH_ERROR
13222
13384
  );
@@ -13444,7 +13606,7 @@ function getClient() {
13444
13606
  if (!config.apiKey) {
13445
13607
  writeError(
13446
13608
  formatError("AUTH_REQUIRED", "No API key configured", {
13447
- suggestion: "Run: mel auth login --api-key <key>"
13609
+ suggestion: `${GET_API_KEY_STEPS}. Then run: mel auth login --api-key mel_...`
13448
13610
  }),
13449
13611
  EXIT_AUTH_ERROR
13450
13612
  );
@@ -13635,6 +13797,8 @@ function registerAuthCommands(program2, getOutputOpts) {
13635
13797
  auth.command("login").description("Store an API key and validate it").requiredOption("--api-key <key>", "API key (mel_...)").addHelpText(
13636
13798
  "after",
13637
13799
  `
13800
+ Get an API key: ${GET_API_KEY_STEPS}.
13801
+
13638
13802
  Returns: { message, user: { id, email, name } }
13639
13803
 
13640
13804
  Examples:
@@ -16386,7 +16550,7 @@ Destination handles for image/video edges to text nodes:
16386
16550
  --dst-handle image when supportedInputs includes "image"
16387
16551
  --dst-handle video when supportedInputs includes "video"
16388
16552
  Models with video+image: Gemini (3.1 Pro, 3 Flash, 2.5 Pro), Qwen 3.5 397B
16389
- Models with image-only: GPT-5.x, Claude Opus/Sonnet, Qwen3 VL 235B
16553
+ Models with image-only: GPT-5.x, Claude Opus/Sonnet, Qwen 3 VL 235B
16390
16554
 
16391
16555
  Destination handles for image edges to video nodes (always set explicitly):
16392
16556
  Derive from the model's variant inputs (mel model list --category video):
@@ -18181,12 +18345,24 @@ Examples:
18181
18345
  }
18182
18346
 
18183
18347
  // src/index.ts
18348
+ function withDefaultHelp(args) {
18349
+ return args.length === 0 ? ["--help"] : args;
18350
+ }
18184
18351
  function createProgram() {
18185
18352
  const program2 = new Command();
18186
- program2.name("mel").description("Mel \u2014 Melius agent CLI").version("0.2.1").showSuggestionAfterError(true).option("--json", "Force JSON output (default)").option("--text", "Human-readable output").option(
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(
18187
18354
  "--fields <fields>",
18188
18355
  "Select specific output fields (comma-separated)"
18189
- ).option("--quiet", "Suppress output, exit code only").option("--verbose", "Log request/response metadata to stderr").addHelpText("after", "\nLearn more: https://docs.melius.com");
18356
+ ).option("--quiet", "Suppress output, exit code only").option("--verbose", "Log request/response metadata to stderr").addHelpText(
18357
+ "after",
18358
+ `
18359
+ Getting started:
18360
+ 1. ${GET_API_KEY_STEPS}
18361
+ 2. mel auth login --api-key mel_...
18362
+ 3. mel project list
18363
+
18364
+ Learn more: https://docs.melius.com`
18365
+ );
18190
18366
  program2.hook("preAction", () => {
18191
18367
  const opts = program2.opts();
18192
18368
  if (opts.verbose) setVerbose(true);
@@ -18248,5 +18424,6 @@ export {
18248
18424
  EXIT_API_ERROR,
18249
18425
  formatError,
18250
18426
  writeError,
18427
+ withDefaultHelp,
18251
18428
  createProgram
18252
18429
  };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Command } from 'commander';
2
2
 
3
+ declare function withDefaultHelp(args: string[]): string[];
3
4
  declare function createProgram(): Command;
4
5
 
5
- export { createProgram };
6
+ export { createProgram, withDefaultHelp };
package/dist/src/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
2
2
  import {
3
- createProgram
4
- } from "../chunk-6IN2VKUC.js";
3
+ createProgram,
4
+ withDefaultHelp
5
+ } from "../chunk-JVD4O5T3.js";
5
6
  export {
6
- createProgram
7
+ createProgram,
8
+ withDefaultHelp
7
9
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@melius-ai/cli",
3
- "version": "0.2.1",
3
+ "version": "0.3.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",