@mindstudio-ai/agent 0.1.5 → 0.1.7

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
@@ -264,6 +264,47 @@ const result = await agent.runAgent({
264
264
 
265
265
  `runAgent()` uses async polling internally — it submits the run, then polls until complete or failed. The poll interval defaults to 1 second and can be configured with `pollIntervalMs`.
266
266
 
267
+ ## Batch execution
268
+
269
+ Execute multiple steps in parallel in a single request. All steps run server-side in parallel — results come back in the same order as the input. Individual failures don't affect other steps.
270
+
271
+ ```typescript
272
+ const { results, totalBillingCost } = await agent.executeStepBatch([
273
+ { stepType: 'generateImage', step: { prompt: 'A mountain landscape' } },
274
+ { stepType: 'textToSpeech', step: { text: 'Welcome to the app' } },
275
+ { stepType: 'searchGoogle', step: { query: 'TypeScript best practices' } },
276
+ ]);
277
+
278
+ // Each result has: stepType, output?, billingCost?, error?
279
+ for (const r of results) {
280
+ if (r.error) {
281
+ console.error(`${r.stepType} failed: ${r.error}`);
282
+ } else {
283
+ console.log(`${r.stepType}:`, r.output);
284
+ }
285
+ }
286
+ ```
287
+
288
+ Maximum 50 steps per batch. Supports `appId` and `threadId` options for thread context.
289
+
290
+ From the CLI:
291
+
292
+ ```bash
293
+ # Inline JSON array
294
+ mindstudio batch '[
295
+ {"stepType": "generateImage", "step": {"prompt": "a sunset"}},
296
+ {"stepType": "searchGoogle", "step": {"query": "cats"}}
297
+ ]'
298
+
299
+ # Piped from a file
300
+ cat steps.json | mindstudio batch
301
+
302
+ # Strip metadata
303
+ mindstudio batch --no-meta '[...]'
304
+ ```
305
+
306
+ Run `mindstudio batch` with no arguments for full usage help.
307
+
267
308
  ## Thread persistence
268
309
 
269
310
  Steps execute within threads. Pass `$threadId` and `$appId` from a previous call to maintain state:
@@ -374,6 +415,10 @@ import type {
374
415
  ListAgentsResult,
375
416
  RunAgentOptions,
376
417
  RunAgentResult,
418
+ BatchStepInput,
419
+ BatchStepResult,
420
+ ExecuteStepBatchOptions,
421
+ ExecuteStepBatchResult,
377
422
  } from '@mindstudio-ai/agent';
378
423
  ```
379
424
 
@@ -411,6 +456,7 @@ Commands:
411
456
  whoami Show current authentication status
412
457
  <method> [json | --flags] Execute a step method
413
458
  exec <method> [json | --flags] Execute a step method (same as above)
459
+ batch [json] Execute multiple steps in parallel
414
460
  list [--json] List available methods
415
461
  info <method> Show method details (params, types, output)
416
462
  agents [--json] List pre-built agents in your organization
package/dist/cli.js CHANGED
@@ -682,8 +682,8 @@ var init_metadata = __esm({
682
682
  "postToX": {
683
683
  stepType: "postToX",
684
684
  description: "Create a post on X (Twitter) from the connected account.",
685
- usageNotes: "- Requires an X OAuth connection (connectionId).\n- Posts are plain text. Maximum 280 characters.",
686
- inputSchema: { "type": "object", "properties": { "text": { "type": "string", "description": "The text content of the post (max 280 characters)" }, "connectionId": { "type": "string", "description": "X (Twitter) OAuth connection ID" } }, "required": ["text"] },
685
+ usageNotes: "- Requires an X OAuth connection (connectionId).\n- Maximum 280 characters of text.\n- Optionally attach up to 4 media items (images, GIFs, or videos) via mediaUrls.\n- Media URLs must be publicly accessible. The service fetches and uploads them to X.\n- Supported formats: JPEG, PNG, GIF, WEBP, MP4. Images up to 5MB, videos up to 512MB.",
686
+ inputSchema: { "type": "object", "properties": { "text": { "type": "string", "description": "The text content of the post (max 280 characters)" }, "connectionId": { "type": "string", "description": "X (Twitter) OAuth connection ID" }, "mediaUrls": { "type": "array", "items": { "type": "string" }, "description": "Up to 4 URLs of images, GIFs, or videos to attach to the post" } }, "required": ["text"] },
687
687
  outputSchema: { "description": "This step does not produce output data." }
688
688
  },
689
689
  "postToZapier": {
@@ -1907,6 +1907,69 @@ var init_client = __esm({
1907
1907
  $billingEvents: billingEvents != null ? JSON.parse(billingEvents) : void 0
1908
1908
  };
1909
1909
  }
1910
+ /**
1911
+ * Execute multiple steps in parallel in a single request.
1912
+ *
1913
+ * All steps run in parallel on the server. Results are returned in the same
1914
+ * order as the input. Individual step failures do not affect other steps —
1915
+ * partial success is possible.
1916
+ *
1917
+ * ```ts
1918
+ * const { results } = await agent.executeStepBatch([
1919
+ * { stepType: 'generateImage', step: { prompt: 'a sunset' } },
1920
+ * { stepType: 'textToSpeech', step: { text: 'Hello world' } },
1921
+ * ]);
1922
+ * ```
1923
+ */
1924
+ async executeStepBatch(steps, options) {
1925
+ const threadId = options?.threadId ?? (this._reuseThreadId ? this._threadId : void 0);
1926
+ const { data } = await request(this._httpConfig, "POST", "/steps/execute-batch", {
1927
+ steps,
1928
+ ...options?.appId != null && { appId: options.appId },
1929
+ ...threadId != null && { threadId }
1930
+ });
1931
+ const results = await Promise.all(
1932
+ data.results.map(async (r) => {
1933
+ if (r.output != null) {
1934
+ return {
1935
+ stepType: r.stepType,
1936
+ output: r.output,
1937
+ billingCost: r.billingCost,
1938
+ error: r.error
1939
+ };
1940
+ }
1941
+ if (r.outputUrl) {
1942
+ const res = await fetch(r.outputUrl);
1943
+ if (!res.ok) {
1944
+ return {
1945
+ stepType: r.stepType,
1946
+ error: `Failed to fetch output from S3: ${res.status} ${res.statusText}`
1947
+ };
1948
+ }
1949
+ const envelope = await res.json();
1950
+ return {
1951
+ stepType: r.stepType,
1952
+ output: envelope.value,
1953
+ billingCost: r.billingCost
1954
+ };
1955
+ }
1956
+ return {
1957
+ stepType: r.stepType,
1958
+ billingCost: r.billingCost,
1959
+ error: r.error
1960
+ };
1961
+ })
1962
+ );
1963
+ if (this._reuseThreadId && data.threadId) {
1964
+ this._threadId = data.threadId;
1965
+ }
1966
+ return {
1967
+ results,
1968
+ totalBillingCost: data.totalBillingCost,
1969
+ appId: data.appId,
1970
+ threadId: data.threadId
1971
+ };
1972
+ }
1910
1973
  /**
1911
1974
  * Get the authenticated user's identity and organization info.
1912
1975
  *
@@ -2211,7 +2274,7 @@ async function startMcpServer(options) {
2211
2274
  capabilities: { tools: {} },
2212
2275
  serverInfo: {
2213
2276
  name: "mindstudio-agent",
2214
- version: "0.1.5"
2277
+ version: "0.1.7"
2215
2278
  },
2216
2279
  instructions: "Welcome to MindStudio \u2014 a platform with 200+ AI models, 850+ third-party integrations, and pre-built agents.\n\nGetting started:\n1. Call `listAgents` to verify your connection and see available agents.\n2. Call `changeName` to set your display name \u2014 use your name or whatever your user calls you. This is how you'll appear in MindStudio request logs.\n3. If you have a profile picture or icon, call `uploadFile` to upload it, then `changeProfilePicture` with the returned URL. This helps users identify your requests in their logs.\n4. Call `listActions` to discover all available actions.\n\nThen use the tools to generate text, images, video, audio, search the web, work with data sources, run agents, and more.\n\nImportant:\n- AI-powered actions (text generation, image generation, video, audio, etc.) cost money. Before running these, call `estimateActionCost` and confirm with the user before proceeding \u2014 unless they've explicitly told you to go ahead.\n- Not all agents from `listAgents` are configured for API use. Do not try to run an agent just because it appears in the list \u2014 it will likely fail. Only run agents the user specifically asks you to run."
2217
2280
  });
@@ -2264,8 +2327,11 @@ async function startMcpServer(options) {
2264
2327
  } else if (toolName === "listConnections") {
2265
2328
  result = await getAgent().listConnections();
2266
2329
  } else if (toolName === "estimateActionCost") {
2330
+ const meta = await getMetadata();
2331
+ const rawType = args.stepType;
2332
+ const resolved = meta[rawType]?.stepType ?? rawType;
2267
2333
  result = await getAgent().estimateStepCost(
2268
- args.stepType,
2334
+ resolved,
2269
2335
  args.step,
2270
2336
  {
2271
2337
  appId: args.appId,
@@ -2292,6 +2358,10 @@ async function startMcpServer(options) {
2292
2358
  extension: ext,
2293
2359
  ...mimeType && { type: mimeType }
2294
2360
  });
2361
+ } else if (toolName === "executeBatch") {
2362
+ result = await getAgent().executeStepBatch(
2363
+ args.steps
2364
+ );
2295
2365
  } else if (toolName === "listAgents") {
2296
2366
  result = await getAgent().listAgents();
2297
2367
  } else if (toolName === "runAgent") {
@@ -2382,7 +2452,8 @@ var init_mcp = __esm({
2382
2452
  changeProfilePicture: "Update the profile picture of the authenticated agent.",
2383
2453
  uploadFile: "Upload a file to the MindStudio CDN.",
2384
2454
  listAgents: "List all pre-built agents in the organization.",
2385
- runAgent: "Run a pre-built agent and wait for the result."
2455
+ runAgent: "Run a pre-built agent and wait for the result.",
2456
+ executeBatch: "Execute multiple actions in parallel in a single request."
2386
2457
  };
2387
2458
  HELPER_TOOLS = [
2388
2459
  {
@@ -2550,6 +2621,37 @@ var init_mcp = __esm({
2550
2621
  required: ["filePath"]
2551
2622
  }
2552
2623
  },
2624
+ {
2625
+ name: "executeBatch",
2626
+ description: "Execute multiple actions in parallel in a single request. All steps run in parallel on the server. Results are returned in the same order as the input. Individual step failures do not affect other steps \u2014 partial success is possible. Maximum 50 steps per batch.",
2627
+ inputSchema: {
2628
+ type: "object",
2629
+ properties: {
2630
+ steps: {
2631
+ type: "array",
2632
+ description: "Array of steps to execute.",
2633
+ minItems: 1,
2634
+ maxItems: 50,
2635
+ items: {
2636
+ type: "object",
2637
+ properties: {
2638
+ stepType: {
2639
+ type: "string",
2640
+ description: 'The action type name (e.g. "generateImage", "textToSpeech").'
2641
+ },
2642
+ step: {
2643
+ type: "object",
2644
+ description: "Action input parameters.",
2645
+ additionalProperties: true
2646
+ }
2647
+ },
2648
+ required: ["stepType", "step"]
2649
+ }
2650
+ }
2651
+ },
2652
+ required: ["steps"]
2653
+ }
2654
+ },
2553
2655
  {
2554
2656
  name: "listAgents",
2555
2657
  description: "List all pre-built agents in the organization along with org metadata.",
@@ -2603,6 +2705,9 @@ Discover:
2603
2705
  info <action> Show action details and parameters
2604
2706
  list-models [--type <t>] [--summary] List available AI models
2605
2707
 
2708
+ Batch:
2709
+ batch [json] Execute multiple actions in parallel
2710
+
2606
2711
  Pre-built agents:
2607
2712
  agents [--json] List agents in your organization
2608
2713
  run-agent <appId> [json | --flags] Run an agent and wait for result
@@ -2645,6 +2750,7 @@ Examples:
2645
2750
  mindstudio list-actions --summary
2646
2751
  mindstudio info generate-image
2647
2752
  mindstudio list-models --type image_generation
2753
+ mindstudio batch '[{"stepType":"generateImage","step":{"prompt":"a cat"}}]'
2648
2754
  mindstudio run-agent <appId> --query "hello"
2649
2755
  mindstudio agents
2650
2756
  mindstudio mcp
@@ -2700,6 +2806,10 @@ function fatal(message) {
2700
2806
  process.stderr.write(JSON.stringify({ error: { message } }) + "\n");
2701
2807
  process.exit(1);
2702
2808
  }
2809
+ function usageBlock(lines) {
2810
+ process.stderr.write("\n" + lines.map((l) => " " + l).join("\n") + "\n\n");
2811
+ process.exit(1);
2812
+ }
2703
2813
  async function readStdin() {
2704
2814
  const chunks = [];
2705
2815
  for await (const chunk of process.stdin) {
@@ -2971,6 +3081,52 @@ async function cmdRun(appId, variables, options) {
2971
3081
  process.stdout.write(JSON.stringify(result, null, 2) + "\n");
2972
3082
  }
2973
3083
  }
3084
+ async function cmdBatch(input, options) {
3085
+ if (!Array.isArray(input)) {
3086
+ fatal(
3087
+ `Batch input must be a JSON array of { stepType, step } objects.
3088
+ Example: mindstudio batch '[{"stepType":"generateImage","step":{"prompt":"a cat"}}]'`
3089
+ );
3090
+ }
3091
+ for (let i = 0; i < input.length; i++) {
3092
+ const item = input[i];
3093
+ if (!item || typeof item !== "object" || !item.stepType || !item.step) {
3094
+ fatal(
3095
+ `Invalid step at index ${i}: each entry must have "stepType" and "step" fields.`
3096
+ );
3097
+ }
3098
+ }
3099
+ const { stepMetadata: stepMetadata2 } = await Promise.resolve().then(() => (init_metadata(), metadata_exports));
3100
+ const metaByName = new Map(
3101
+ Object.entries(stepMetadata2).map(([name, m]) => [name, m])
3102
+ );
3103
+ const steps = input.map(
3104
+ (item, i) => {
3105
+ let meta = metaByName.get(item.stepType);
3106
+ if (!meta) {
3107
+ const camel = item.stepType.replace(
3108
+ /-([a-z])/g,
3109
+ (_, c) => c.toUpperCase()
3110
+ );
3111
+ meta = metaByName.get(camel);
3112
+ }
3113
+ if (meta) {
3114
+ return { stepType: meta.stepType, step: item.step };
3115
+ }
3116
+ return { stepType: item.stepType, step: item.step };
3117
+ }
3118
+ );
3119
+ const agent = await createAgent(options);
3120
+ const result = await agent.executeStepBatch(steps, {
3121
+ appId: options.appId,
3122
+ threadId: options.threadId
3123
+ });
3124
+ if (options.noMeta) {
3125
+ process.stdout.write(JSON.stringify(result.results, null, 2) + "\n");
3126
+ } else {
3127
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
3128
+ }
3129
+ }
2974
3130
  var MIME_TYPES2 = {
2975
3131
  png: "image/png",
2976
3132
  jpg: "image/jpeg",
@@ -3026,7 +3182,7 @@ function isNewerVersion(current, latest) {
3026
3182
  return false;
3027
3183
  }
3028
3184
  async function checkForUpdate() {
3029
- const currentVersion = "0.1.5";
3185
+ const currentVersion = "0.1.7";
3030
3186
  if (!currentVersion) return null;
3031
3187
  try {
3032
3188
  const { loadConfig: loadConfig2, saveConfig: saveConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
@@ -3055,7 +3211,7 @@ async function checkForUpdate() {
3055
3211
  }
3056
3212
  }
3057
3213
  function printUpdateNotice(latestVersion) {
3058
- const currentVersion = "0.1.5";
3214
+ const currentVersion = "0.1.7";
3059
3215
  process.stderr.write(
3060
3216
  `
3061
3217
  ${ansi.cyanBright("Update available")} ${ansi.gray(currentVersion + " \u2192")} ${ansi.cyanBold(latestVersion)}
@@ -3130,7 +3286,7 @@ async function cmdLogin(options) {
3130
3286
  process.stderr.write("\n");
3131
3287
  printLogo();
3132
3288
  process.stderr.write("\n");
3133
- const ver = "0.1.5";
3289
+ const ver = "0.1.7";
3134
3290
  process.stderr.write(
3135
3291
  ` ${ansi.bold("MindStudio Agent")} ${ver ? " " + ansi.gray("v" + ver) : ""}
3136
3292
  `
@@ -3463,10 +3619,82 @@ async function main() {
3463
3619
  });
3464
3620
  return;
3465
3621
  }
3622
+ if (command === "batch") {
3623
+ let input2;
3624
+ const firstArg = positionals[1];
3625
+ if (firstArg && firstArg.startsWith("[")) {
3626
+ try {
3627
+ input2 = parseJson5(firstArg);
3628
+ } catch {
3629
+ fatal(`Invalid JSON input: ${firstArg}`);
3630
+ }
3631
+ } else if (!process.stdin.isTTY) {
3632
+ const raw = (await readStdin()).trim();
3633
+ if (raw) {
3634
+ try {
3635
+ input2 = parseJson5(raw);
3636
+ } catch {
3637
+ fatal(`Invalid JSON on stdin: ${raw}`);
3638
+ }
3639
+ }
3640
+ }
3641
+ if (input2 === void 0) {
3642
+ usageBlock([
3643
+ "batch \u2014 Execute multiple actions in parallel",
3644
+ "",
3645
+ "Usage:",
3646
+ ` mindstudio batch '[{ "stepType": "<action>", "step": { ... } }, ...]'`,
3647
+ " cat steps.json | mindstudio batch",
3648
+ "",
3649
+ 'Each entry needs "stepType" (action name) and "step" (input object).',
3650
+ "Maximum 50 steps per batch. Results come back in the same order.",
3651
+ "Individual failures don't affect other steps.",
3652
+ "",
3653
+ "Options:",
3654
+ " --app-id <id> App ID for thread context",
3655
+ " --thread-id <id> Thread ID for state persistence",
3656
+ " --no-meta Strip top-level metadata from output",
3657
+ "",
3658
+ "Examples:",
3659
+ " mindstudio batch '[",
3660
+ ' { "stepType": "generateImage", "step": { "prompt": "a sunset" } },',
3661
+ ' { "stepType": "textToSpeech", "step": { "text": "hello world" } }',
3662
+ " ]'",
3663
+ "",
3664
+ ` echo '[{"stepType":"searchGoogle","step":{"query":"cats"}}]' | mindstudio batch`
3665
+ ]);
3666
+ }
3667
+ await cmdBatch(input2, {
3668
+ apiKey: values["api-key"],
3669
+ baseUrl: values["base-url"],
3670
+ appId: values["app-id"],
3671
+ threadId: values["thread-id"],
3672
+ noMeta: values["no-meta"]
3673
+ });
3674
+ return;
3675
+ }
3466
3676
  if (command === "run-agent") {
3467
3677
  const appId = positionals[1];
3468
3678
  if (!appId)
3469
- fatal("Missing app ID. Usage: mindstudio run-agent <appId> [json | --flags]");
3679
+ usageBlock([
3680
+ "run-agent \u2014 Run a pre-built agent and wait for the result",
3681
+ "",
3682
+ "Usage:",
3683
+ " mindstudio run-agent <appId> [json | --flags]",
3684
+ "",
3685
+ "Options:",
3686
+ " --workflow <name> Workflow to execute (default: app default)",
3687
+ ' --version <ver> App version, e.g. "draft" (default: "live")',
3688
+ " --output-key <key> Extract a single field from the result",
3689
+ " --no-meta Strip metadata from output",
3690
+ "",
3691
+ "Examples:",
3692
+ ' mindstudio run-agent abc123 --query "hello"',
3693
+ ` mindstudio run-agent abc123 '{"query": "hello"}'`,
3694
+ " mindstudio run-agent abc123 --workflow summarize --version draft",
3695
+ "",
3696
+ 'Tip: run "mindstudio agents" to list available agent IDs.'
3697
+ ]);
3470
3698
  const runArgv = process.argv.slice(process.argv.indexOf("run-agent") + 2);
3471
3699
  const stepArgs = [];
3472
3700
  for (let i = 0; i < runArgv.length; i++) {
@@ -3515,7 +3743,18 @@ async function main() {
3515
3743
  if (command === "upload") {
3516
3744
  const filePath = positionals[1];
3517
3745
  if (!filePath)
3518
- fatal("Missing file path. Usage: mindstudio upload <filepath>");
3746
+ usageBlock([
3747
+ "upload \u2014 Upload a file to the MindStudio CDN",
3748
+ "",
3749
+ "Usage:",
3750
+ " mindstudio upload <filepath>",
3751
+ "",
3752
+ "Returns the permanent public URL for the uploaded file.",
3753
+ "",
3754
+ "Examples:",
3755
+ " mindstudio upload photo.png",
3756
+ " mindstudio upload /path/to/document.pdf"
3757
+ ]);
3519
3758
  await cmdUpload(filePath, {
3520
3759
  apiKey: values["api-key"],
3521
3760
  baseUrl: values["base-url"]
@@ -3532,7 +3771,20 @@ async function main() {
3532
3771
  if (command === "list-models-by-type" || command === "list-models-summary-by-type") {
3533
3772
  type = positionals[1];
3534
3773
  if (!type)
3535
- fatal(`Missing model type. Usage: mindstudio ${command} <type>`);
3774
+ usageBlock([
3775
+ `${command} \u2014 List AI models filtered by type`,
3776
+ "",
3777
+ "Usage:",
3778
+ ` mindstudio ${command} <type>`,
3779
+ "",
3780
+ "Types:",
3781
+ " llm_chat, image_generation, video_generation,",
3782
+ " video_analysis, text_to_speech, vision, transcription",
3783
+ "",
3784
+ "Examples:",
3785
+ ` mindstudio ${command} image_generation`,
3786
+ ` mindstudio ${command} llm_chat`
3787
+ ]);
3536
3788
  }
3537
3789
  if (command === "list-models-summary" || command === "list-models-summary-by-type") {
3538
3790
  summary = true;
@@ -3562,9 +3814,22 @@ async function main() {
3562
3814
  if (command === "estimate-cost") {
3563
3815
  const stepMethod = positionals[1];
3564
3816
  if (!stepMethod)
3565
- fatal(
3566
- "Missing action name. Usage: mindstudio estimate-cost <action> [json | --flags]"
3567
- );
3817
+ usageBlock([
3818
+ "estimate-cost \u2014 Estimate the cost of an action before running it",
3819
+ "",
3820
+ "Usage:",
3821
+ " mindstudio estimate-cost <action> [json | --flags]",
3822
+ "",
3823
+ "Examples:",
3824
+ ' mindstudio estimate-cost generate-image --prompt "a sunset"',
3825
+ ` mindstudio estimate-cost generate-text '{"message": "hello"}'`,
3826
+ "",
3827
+ 'Tip: run "mindstudio list-actions" to see available actions.'
3828
+ ]);
3829
+ const allKeys2 = await getAllMethodKeys();
3830
+ const resolvedMethod = resolveMethodOrFail(stepMethod, allKeys2);
3831
+ const { stepMetadata: stepMetadata2 } = await Promise.resolve().then(() => (init_metadata(), metadata_exports));
3832
+ const meta = stepMetadata2[resolvedMethod];
3568
3833
  const costArgv = positionals.slice(2);
3569
3834
  let costInput;
3570
3835
  const firstArg = costArgv[0];
@@ -3577,7 +3842,7 @@ async function main() {
3577
3842
  } else {
3578
3843
  costInput = parseStepFlags(costArgv);
3579
3844
  }
3580
- await cmdEstimateStepCost(stepMethod, costInput, {
3845
+ await cmdEstimateStepCost(meta.stepType, costInput, {
3581
3846
  apiKey: values["api-key"],
3582
3847
  baseUrl: values["base-url"]
3583
3848
  });
@@ -3586,7 +3851,15 @@ async function main() {
3586
3851
  if (command === "change-name") {
3587
3852
  const name = positionals[1];
3588
3853
  if (!name)
3589
- fatal("Missing name. Usage: mindstudio change-name <name>");
3854
+ usageBlock([
3855
+ "change-name \u2014 Update your display name",
3856
+ "",
3857
+ "Usage:",
3858
+ " mindstudio change-name <name>",
3859
+ "",
3860
+ "Examples:",
3861
+ ' mindstudio change-name "My Agent"'
3862
+ ]);
3590
3863
  await cmdChangeName(name, {
3591
3864
  apiKey: values["api-key"],
3592
3865
  baseUrl: values["base-url"]
@@ -3596,9 +3869,17 @@ async function main() {
3596
3869
  if (command === "change-profile-picture") {
3597
3870
  const url = positionals[1];
3598
3871
  if (!url)
3599
- fatal(
3600
- "Missing URL. Usage: mindstudio change-profile-picture <url>"
3601
- );
3872
+ usageBlock([
3873
+ "change-profile-picture \u2014 Update your profile picture",
3874
+ "",
3875
+ "Usage:",
3876
+ " mindstudio change-profile-picture <url>",
3877
+ "",
3878
+ "Examples:",
3879
+ " mindstudio change-profile-picture https://example.com/avatar.png",
3880
+ "",
3881
+ 'Tip: use "mindstudio upload" to host an image first.'
3882
+ ]);
3602
3883
  await cmdChangeProfilePicture(url, {
3603
3884
  apiKey: values["api-key"],
3604
3885
  baseUrl: values["base-url"]
@@ -3616,13 +3897,48 @@ async function main() {
3616
3897
  if (command === "info") {
3617
3898
  const rawMethod2 = positionals[1];
3618
3899
  if (!rawMethod2)
3619
- fatal("Missing action name. Usage: mindstudio info <action>");
3900
+ usageBlock([
3901
+ "info \u2014 Show action details and parameters",
3902
+ "",
3903
+ "Usage:",
3904
+ " mindstudio info <action>",
3905
+ "",
3906
+ "Shows the description, input parameters (with types and",
3907
+ "defaults), and output fields for an action.",
3908
+ "",
3909
+ "Examples:",
3910
+ " mindstudio info generate-image",
3911
+ " mindstudio info search-google",
3912
+ "",
3913
+ 'Tip: run "mindstudio list-actions" to see available actions.'
3914
+ ]);
3620
3915
  await cmdInfo(rawMethod2);
3621
3916
  return;
3622
3917
  }
3623
3918
  const split = findMethodSplit(process.argv.slice(2));
3624
3919
  if (!split)
3625
- fatal("Missing action name. Usage: mindstudio <action> [json | --flags]");
3920
+ usageBlock([
3921
+ "Run an action directly",
3922
+ "",
3923
+ "Usage:",
3924
+ " mindstudio <action> [json | --flags]",
3925
+ " mindstudio run <action> [json | --flags]",
3926
+ "",
3927
+ "Input can be inline JSON, --flags, or piped via stdin.",
3928
+ "",
3929
+ "Options:",
3930
+ " --app-id <id> App ID for thread context",
3931
+ " --thread-id <id> Thread ID for state persistence",
3932
+ " --output-key <key> Extract a single field from the result",
3933
+ " --no-meta Strip $-prefixed metadata from output",
3934
+ "",
3935
+ "Examples:",
3936
+ ' mindstudio generate-image --prompt "a sunset"',
3937
+ ` mindstudio search-google '{"query": "cats"}'`,
3938
+ ` echo '{"message":"hello"}' | mindstudio generate-text`,
3939
+ "",
3940
+ 'Tip: run "mindstudio list-actions" to see available actions.'
3941
+ ]);
3626
3942
  const { rawMethod, stepArgv } = split;
3627
3943
  const allKeys = await getAllMethodKeys();
3628
3944
  const method = resolveMethodOrFail(rawMethod, allKeys);