@mindstudio-ai/agent 0.0.16 → 0.0.17

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
@@ -46,8 +46,8 @@ Every method is fully typed — your editor will autocomplete available paramete
46
46
  ### CLI
47
47
 
48
48
  ```bash
49
- # Set your API key
50
- export MINDSTUDIO_API_KEY=your-api-key
49
+ # Authenticate (opens browser, saves key locally)
50
+ mindstudio login
51
51
 
52
52
  # Execute a step with named flags
53
53
  mindstudio generate-image --prompt "A mountain landscape at sunset"
@@ -96,9 +96,15 @@ All 120+ step methods are exposed as MCP tools with full JSON Schema input defin
96
96
 
97
97
  ## Authentication
98
98
 
99
- The SDK supports two authentication modes:
99
+ The fastest way to authenticate is the interactive login:
100
100
 
101
- **API Key** — for external apps, scripts, and CLI usage:
101
+ ```bash
102
+ mindstudio login
103
+ ```
104
+
105
+ This opens your browser, authenticates with MindStudio, and saves your API key to `~/.mindstudio/config.json`. All subsequent CLI and SDK usage will pick it up automatically.
106
+
107
+ You can also authenticate via environment variable or constructor parameter:
102
108
 
103
109
  ```typescript
104
110
  // Pass directly
@@ -109,15 +115,19 @@ const agent = new MindStudioAgent({ apiKey: 'your-api-key' });
109
115
  const agent = new MindStudioAgent();
110
116
  ```
111
117
 
112
- **Managed mode**automatically available inside MindStudio custom functions:
118
+ MindStudio routes to the correct AI provider (OpenAI, Google, Anthropic, etc.) server-side you do not need separate provider API keys.
113
119
 
114
- ```typescript
115
- // Inside a MindStudio custom function, auth and base URL are automatic
116
- // (CALLBACK_TOKEN and REMOTE_HOSTNAME are set by the runtime)
117
- const agent = new MindStudioAgent();
120
+ Other auth commands:
121
+
122
+ ```bash
123
+ # Check current auth status and verify credentials
124
+ mindstudio whoami
125
+
126
+ # Clear stored credentials
127
+ mindstudio logout
118
128
  ```
119
129
 
120
- Resolution order: constructor `apiKey` > `MINDSTUDIO_API_KEY` env > `CALLBACK_TOKEN` env.
130
+ Resolution order: constructor `apiKey` > `MINDSTUDIO_API_KEY` env > `~/.mindstudio/config.json` > `CALLBACK_TOKEN` env.
121
131
 
122
132
  ## Thread persistence
123
133
 
@@ -248,7 +258,7 @@ const { services } = await agent.listConnectors();
248
258
 
249
259
  ```typescript
250
260
  const agent = new MindStudioAgent({
251
- // API key (or set MINDSTUDIO_API_KEY env var)
261
+ // API key (or set MINDSTUDIO_API_KEY env var, or run `mindstudio login`)
252
262
  apiKey: 'your-api-key',
253
263
 
254
264
  // Base URL (or set MINDSTUDIO_BASE_URL env var)
@@ -322,6 +332,9 @@ import { blockTypeAliases } from '@mindstudio-ai/agent';
322
332
  Usage: mindstudio <command | method> [options]
323
333
 
324
334
  Commands:
335
+ login Authenticate with MindStudio (opens browser)
336
+ logout Clear stored credentials
337
+ whoami Show current authentication status
325
338
  <method> [json | --flags] Execute a step method
326
339
  exec <method> [json | --flags] Execute a step method (same as above)
327
340
  list [--json] List available methods
package/dist/cli.js CHANGED
@@ -845,9 +845,9 @@ var init_metadata = __esm({
845
845
  },
846
846
  "sendSMS": {
847
847
  stepType: "sendSMS",
848
- description: "Send an SMS text message to a phone number configured via OAuth connection.",
849
- usageNotes: "- User is responsible for configuring the connection to the number (MindStudio requires double opt-in to prevent spam)",
850
- inputSchema: { "type": "object", "properties": { "body": { "type": "string", "description": "SMS message body text" }, "connectionId": { "type": "string", "description": "OAuth connection ID for the recipient phone number" } }, "required": ["body"] },
848
+ description: "Send an SMS or MMS message to a phone number configured via OAuth connection.",
849
+ usageNotes: "- User is responsible for configuring the connection to the number (MindStudio requires double opt-in to prevent spam)\n- If mediaUrls are provided, the message is sent as MMS instead of SMS\n- MMS supports up to 10 media URLs (images, video, audio, PDF) with a 5MB limit per file\n- MMS is only supported on US and Canadian carriers; international numbers will receive SMS only (media silently dropped)",
850
+ inputSchema: { "type": "object", "properties": { "body": { "type": "string", "description": "SMS message body text" }, "connectionId": { "type": "string", "description": "OAuth connection ID for the recipient phone number" }, "mediaUrls": { "type": "array", "items": { "type": "string" }, "description": "Optional array of media URLs to send as MMS (up to 10, 5MB each)" } }, "required": ["body"] },
851
851
  outputSchema: { "description": "This step does not produce output data." }
852
852
  },
853
853
  "setRunTitle": {
@@ -864,6 +864,20 @@ var init_metadata = __esm({
864
864
  inputSchema: { "type": "object", "properties": { "value": { "anyOf": [{ "type": "string" }, { "type": "array", "items": { "type": "string" } }] } }, "required": ["value"], "description": "Configuration for the set variable step" },
865
865
  outputSchema: { "type": "object" }
866
866
  },
867
+ "telegramEditMessage": {
868
+ stepType: "telegramEditMessage",
869
+ description: "Edit a previously sent Telegram message. Use with the message ID returned by Send Telegram Message.",
870
+ usageNotes: '- Only text messages sent by the bot can be edited.\n- The messageId is returned by the Send Telegram Message step.\n- Common pattern: send a "Processing..." message, do work, then edit it with the result.',
871
+ inputSchema: { "type": "object", "properties": { "botToken": { "type": "string", "description": 'Telegram bot token in "botId:token" format' }, "chatId": { "type": "string", "description": "Telegram chat ID containing the message" }, "messageId": { "type": "string", "description": "ID of the message to edit" }, "text": { "type": "string", "description": "New message text (MarkdownV2 formatting supported)" } }, "required": ["botToken", "chatId", "messageId", "text"] },
872
+ outputSchema: { "description": "This step does not produce output data." }
873
+ },
874
+ "telegramReplyToMessage": {
875
+ stepType: "telegramReplyToMessage",
876
+ description: "Send a reply to a specific Telegram message. The reply will be visually threaded in the chat.",
877
+ usageNotes: "- Use the rawMessage.message_id from the incoming trigger variables to reply to the user's message.\n- Especially useful in group chats where replies provide context.\n- Returns the sent message ID, which can be used with Edit Telegram Message.",
878
+ inputSchema: { "type": "object", "properties": { "botToken": { "type": "string", "description": 'Telegram bot token in "botId:token" format' }, "chatId": { "type": "string", "description": "Telegram chat ID to send the reply to" }, "replyToMessageId": { "type": "string", "description": "ID of the message to reply to" }, "text": { "type": "string", "description": "Reply text (MarkdownV2 formatting supported)" } }, "required": ["botToken", "chatId", "replyToMessageId", "text"] },
879
+ outputSchema: { "type": "object", "properties": { "messageId": { "type": "number", "description": "ID of the sent reply message" } }, "required": ["messageId"] }
880
+ },
867
881
  "telegramSendAudio": {
868
882
  stepType: "telegramSendAudio",
869
883
  description: "Send an audio file to a Telegram chat as music or a voice note via a bot.",
@@ -888,9 +902,9 @@ var init_metadata = __esm({
888
902
  "telegramSendMessage": {
889
903
  stepType: "telegramSendMessage",
890
904
  description: "Send a text message to a Telegram chat via a bot.",
891
- usageNotes: '- Messages are sent using MarkdownV2 formatting. Special characters are auto-escaped.\n- botToken format is "botId:token" \u2014 both parts are required.',
905
+ usageNotes: '- Messages are sent using MarkdownV2 formatting. Special characters are auto-escaped.\n- botToken format is "botId:token" \u2014 both parts are required.\n- Returns the sent message ID, which can be used with Edit Telegram Message to update the message later.',
892
906
  inputSchema: { "type": "object", "properties": { "botToken": { "type": "string", "description": 'Telegram bot token in "botId:token" format' }, "chatId": { "type": "string", "description": "Telegram chat ID to send the message to" }, "text": { "type": "string", "description": "Message text to send (MarkdownV2 formatting supported)" } }, "required": ["botToken", "chatId", "text"] },
893
- outputSchema: { "description": "This step does not produce output data." }
907
+ outputSchema: { "type": "object", "properties": { "messageId": { "type": "number", "description": "ID of the sent Telegram message" } }, "required": ["messageId"] }
894
908
  },
895
909
  "telegramSendVideo": {
896
910
  stepType: "telegramSendVideo",
@@ -1159,6 +1173,48 @@ var init_rate_limit = __esm({
1159
1173
  }
1160
1174
  });
1161
1175
 
1176
+ // src/config.ts
1177
+ var config_exports = {};
1178
+ __export(config_exports, {
1179
+ clearConfig: () => clearConfig,
1180
+ getConfigPath: () => getConfigPath,
1181
+ loadConfig: () => loadConfig,
1182
+ saveConfig: () => saveConfig
1183
+ });
1184
+ import { readFileSync, writeFileSync, mkdirSync } from "fs";
1185
+ import { join } from "path";
1186
+ import { homedir } from "os";
1187
+ function getConfigPath() {
1188
+ return CONFIG_PATH;
1189
+ }
1190
+ function loadConfig() {
1191
+ try {
1192
+ const raw = readFileSync(CONFIG_PATH, "utf-8");
1193
+ return JSON.parse(raw);
1194
+ } catch {
1195
+ return {};
1196
+ }
1197
+ }
1198
+ function saveConfig(config) {
1199
+ mkdirSync(CONFIG_DIR, { recursive: true });
1200
+ writeFileSync(
1201
+ CONFIG_PATH,
1202
+ JSON.stringify(config, null, 2) + "\n",
1203
+ "utf-8"
1204
+ );
1205
+ }
1206
+ function clearConfig() {
1207
+ saveConfig({});
1208
+ }
1209
+ var CONFIG_DIR, CONFIG_PATH;
1210
+ var init_config = __esm({
1211
+ "src/config.ts"() {
1212
+ "use strict";
1213
+ CONFIG_DIR = join(homedir(), ".mindstudio");
1214
+ CONFIG_PATH = join(CONFIG_DIR, "config.json");
1215
+ }
1216
+ });
1217
+
1162
1218
  // src/generated/steps.ts
1163
1219
  var steps_exports = {};
1164
1220
  __export(steps_exports, {
@@ -1520,6 +1576,12 @@ function applyStepMethods(AgentClass) {
1520
1576
  proto.setVariable = function(step, options) {
1521
1577
  return this.executeStep("setVariable", step, options);
1522
1578
  };
1579
+ proto.telegramEditMessage = function(step, options) {
1580
+ return this.executeStep("telegramEditMessage", step, options);
1581
+ };
1582
+ proto.telegramReplyToMessage = function(step, options) {
1583
+ return this.executeStep("telegramReplyToMessage", step, options);
1584
+ };
1523
1585
  proto.telegramSendAudio = function(step, options) {
1524
1586
  return this.executeStep("telegramSendAudio", step, options);
1525
1587
  };
@@ -1624,14 +1686,16 @@ __export(client_exports, {
1624
1686
  function sleep2(ms) {
1625
1687
  return new Promise((resolve) => setTimeout(resolve, ms));
1626
1688
  }
1627
- function resolveToken(provided) {
1689
+ function resolveToken(provided, config) {
1628
1690
  if (provided) return { token: provided, authType: "apiKey" };
1629
1691
  if (process.env.MINDSTUDIO_API_KEY)
1630
1692
  return { token: process.env.MINDSTUDIO_API_KEY, authType: "apiKey" };
1693
+ if (config?.apiKey)
1694
+ return { token: config.apiKey, authType: "apiKey" };
1631
1695
  if (process.env.CALLBACK_TOKEN)
1632
1696
  return { token: process.env.CALLBACK_TOKEN, authType: "internal" };
1633
1697
  throw new MindStudioError(
1634
- "No API key provided. Pass `apiKey` to the MindStudioAgent constructor, or set the MINDSTUDIO_API_KEY environment variable.",
1698
+ "No API key provided. Run `mindstudio login`, pass `apiKey` to the constructor, or set the MINDSTUDIO_API_KEY environment variable.",
1635
1699
  "missing_api_key",
1636
1700
  401
1637
1701
  );
@@ -1643,6 +1707,7 @@ var init_client = __esm({
1643
1707
  init_http();
1644
1708
  init_errors();
1645
1709
  init_rate_limit();
1710
+ init_config();
1646
1711
  init_steps();
1647
1712
  init_helpers();
1648
1713
  DEFAULT_BASE_URL = "https://v1.mindstudio-api.com";
@@ -1655,8 +1720,9 @@ var init_client = __esm({
1655
1720
  /** @internal */
1656
1721
  _threadId;
1657
1722
  constructor(options = {}) {
1658
- const { token, authType } = resolveToken(options.apiKey);
1659
- const baseUrl = options.baseUrl ?? process.env.MINDSTUDIO_BASE_URL ?? process.env.REMOTE_HOSTNAME ?? DEFAULT_BASE_URL;
1723
+ const config = loadConfig();
1724
+ const { token, authType } = resolveToken(options.apiKey, config);
1725
+ const baseUrl = options.baseUrl ?? process.env.MINDSTUDIO_BASE_URL ?? process.env.REMOTE_HOSTNAME ?? config.baseUrl ?? DEFAULT_BASE_URL;
1660
1726
  this._reuseThreadId = options.reuseThreadId ?? /^(true|1)$/i.test(process.env.MINDSTUDIO_REUSE_THREAD_ID ?? "");
1661
1727
  this._httpConfig = {
1662
1728
  baseUrl,
@@ -1858,7 +1924,7 @@ async function startMcpServer(options) {
1858
1924
  capabilities: { tools: {} },
1859
1925
  serverInfo: {
1860
1926
  name: "mindstudio-agent",
1861
- version: "0.0.16"
1927
+ version: "0.0.17"
1862
1928
  }
1863
1929
  });
1864
1930
  break;
@@ -2024,9 +2090,13 @@ var init_mcp = __esm({
2024
2090
 
2025
2091
  // src/cli.ts
2026
2092
  import { parseArgs } from "util";
2093
+ import { execSync } from "child_process";
2027
2094
  var HELP = `Usage: mindstudio <command | method> [options]
2028
2095
 
2029
2096
  Commands:
2097
+ login Authenticate with MindStudio (opens browser)
2098
+ logout Clear stored credentials
2099
+ whoami Show current authentication status
2030
2100
  <method> [json | --flags] Execute a step method (shorthand for exec)
2031
2101
  exec <method> [json | --flags] Execute a step method
2032
2102
  list [--json] List available methods
@@ -2048,6 +2118,7 @@ Options:
2048
2118
  --help Show this help
2049
2119
 
2050
2120
  Examples:
2121
+ mindstudio login
2051
2122
  mindstudio generate-image --prompt "a sunset"
2052
2123
  mindstudio generate-image --prompt "a sunset" --output-key imageUrl
2053
2124
  mindstudio generate-text --message "hello" --no-meta
@@ -2358,6 +2429,300 @@ async function cmdRun(appId, variables, options) {
2358
2429
  process.stdout.write(JSON.stringify(result, null, 2) + "\n");
2359
2430
  }
2360
2431
  }
2432
+ var ansi = {
2433
+ cyan: (s) => `\x1B[36m${s}\x1B[0m`,
2434
+ cyanBright: (s) => `\x1B[96m${s}\x1B[0m`,
2435
+ cyanBold: (s) => `\x1B[96;1m${s}\x1B[0m`,
2436
+ dim: (s) => `\x1B[2m${s}\x1B[0m`,
2437
+ green: (s) => `\x1B[32m${s}\x1B[0m`,
2438
+ greenBold: (s) => `\x1B[32;1m${s}\x1B[0m`,
2439
+ gray: (s) => `\x1B[90m${s}\x1B[0m`,
2440
+ bold: (s) => `\x1B[1m${s}\x1B[0m`
2441
+ };
2442
+ var UPDATE_CHECK_INTERVAL = 60 * 60 * 1e3;
2443
+ function isNewerVersion(current, latest) {
2444
+ const c = current.split(".").map(Number);
2445
+ const l = latest.split(".").map(Number);
2446
+ for (let i = 0; i < Math.max(c.length, l.length); i++) {
2447
+ const cv = c[i] ?? 0;
2448
+ const lv = l[i] ?? 0;
2449
+ if (lv > cv) return true;
2450
+ if (lv < cv) return false;
2451
+ }
2452
+ return false;
2453
+ }
2454
+ async function checkForUpdate() {
2455
+ const currentVersion = "0.0.17";
2456
+ if (!currentVersion) return null;
2457
+ try {
2458
+ const { loadConfig: loadConfig2, saveConfig: saveConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
2459
+ const config = loadConfig2();
2460
+ if (config._updateCheck) {
2461
+ const age = Date.now() - config._updateCheck.checkedAt;
2462
+ if (age < UPDATE_CHECK_INTERVAL) {
2463
+ return isNewerVersion(currentVersion, config._updateCheck.latestVersion) ? config._updateCheck.latestVersion : null;
2464
+ }
2465
+ }
2466
+ const res = await fetch(
2467
+ "https://registry.npmjs.org/@mindstudio-ai/agent/latest",
2468
+ { signal: AbortSignal.timeout(5e3) }
2469
+ );
2470
+ if (!res.ok) return null;
2471
+ const data = await res.json();
2472
+ const latestVersion = data.version;
2473
+ if (!latestVersion) return null;
2474
+ saveConfig2({
2475
+ ...config,
2476
+ _updateCheck: { latestVersion, checkedAt: Date.now() }
2477
+ });
2478
+ return isNewerVersion(currentVersion, latestVersion) ? latestVersion : null;
2479
+ } catch {
2480
+ return null;
2481
+ }
2482
+ }
2483
+ function printUpdateNotice(latestVersion) {
2484
+ const currentVersion = "0.0.17";
2485
+ process.stderr.write(
2486
+ `
2487
+ ${ansi.cyanBright("Update available")} ${ansi.gray(currentVersion + " \u2192")} ${ansi.cyanBold(latestVersion)}
2488
+ ${ansi.gray("Run")} npm install -g @mindstudio-ai/agent ${ansi.gray("to update")}
2489
+ `
2490
+ );
2491
+ }
2492
+ var LOGO = ` .=+-. :++.
2493
+ *@@@@@+ :%@@@@%:
2494
+ .%@@@@@@#..@@@@@@@=
2495
+ .*@@@@@@@--@@@@@@@#.**.
2496
+ *@@@@@@@.-@@@@@@@@.#@@*
2497
+ .#@@@@@@@-.@@@@@@@* #@@@@%.
2498
+ =@@@@@@@-.@@@@@@@#.-@@@@@@+
2499
+ :@@@@@@: +@@@@@#. .@@@@@@:
2500
+ .++: .-*-. .++:`;
2501
+ function printLogo() {
2502
+ const lines = LOGO.split("\n");
2503
+ for (const line of lines) {
2504
+ const colored = line.replace(
2505
+ /[^\s]/g,
2506
+ (ch) => ch === "." || ch === ":" || ch === "-" || ch === "+" || ch === "=" ? `\x1B[36m${ch}\x1B[0m` : `\x1B[96;1m${ch}\x1B[0m`
2507
+ );
2508
+ process.stderr.write(` ${colored}
2509
+ `);
2510
+ }
2511
+ }
2512
+ function openBrowser(url) {
2513
+ try {
2514
+ if (process.platform === "darwin") execSync(`open "${url}"`);
2515
+ else if (process.platform === "win32") execSync(`start "" "${url}"`);
2516
+ else execSync(`xdg-open "${url}"`);
2517
+ } catch {
2518
+ }
2519
+ }
2520
+ function sleep3(ms) {
2521
+ return new Promise((resolve) => setTimeout(resolve, ms));
2522
+ }
2523
+ function waitForKeypress() {
2524
+ return new Promise((resolve) => {
2525
+ if (!process.stdin.isTTY) {
2526
+ resolve();
2527
+ return;
2528
+ }
2529
+ process.stdin.setRawMode(true);
2530
+ process.stdin.resume();
2531
+ process.stdin.once("data", () => {
2532
+ process.stdin.setRawMode(false);
2533
+ process.stdin.pause();
2534
+ resolve();
2535
+ });
2536
+ });
2537
+ }
2538
+ function maskKey(key) {
2539
+ if (key.length <= 8) return "****";
2540
+ return key.slice(0, 4) + "..." + key.slice(-4);
2541
+ }
2542
+ var DEFAULT_BASE_URL2 = "https://v1.mindstudio-api.com";
2543
+ var SPINNER_FRAMES = [
2544
+ "\u28FE",
2545
+ "\u28FD",
2546
+ "\u28FB",
2547
+ "\u28BF",
2548
+ "\u287F",
2549
+ "\u28DF",
2550
+ "\u28EF",
2551
+ "\u28F7"
2552
+ ];
2553
+ async function cmdLogin(options) {
2554
+ const baseUrl = options.baseUrl ?? process.env.MINDSTUDIO_BASE_URL ?? process.env.REMOTE_HOSTNAME ?? DEFAULT_BASE_URL2;
2555
+ process.stderr.write("\n");
2556
+ printLogo();
2557
+ process.stderr.write("\n");
2558
+ const ver = "0.0.17";
2559
+ process.stderr.write(
2560
+ ` ${ansi.bold("MindStudio")} ${ansi.gray("CLI")}${ver ? " " + ansi.gray("v" + ver) : ""}
2561
+ `
2562
+ );
2563
+ process.stderr.write(
2564
+ ` ${ansi.gray("Connect your MindStudio account to get started.")}
2565
+
2566
+ `
2567
+ );
2568
+ process.stderr.write(
2569
+ ` ${ansi.cyanBright("Press any key to open the browser...")}
2570
+
2571
+
2572
+
2573
+ `
2574
+ );
2575
+ await waitForKeypress();
2576
+ process.stderr.write("\x1B[4A\r\x1B[J");
2577
+ process.stderr.write(
2578
+ ` ${ansi.gray("Requesting authorization...")}
2579
+ `
2580
+ );
2581
+ const authRes = await fetch(
2582
+ `${baseUrl}/developer/v2/request-auth-url`,
2583
+ {
2584
+ headers: {
2585
+ "Content-Type": "application/json",
2586
+ "User-Agent": "@mindstudio-ai/agent"
2587
+ }
2588
+ }
2589
+ );
2590
+ if (!authRes.ok) {
2591
+ fatal(
2592
+ `Failed to request auth URL: ${authRes.status} ${authRes.statusText}`
2593
+ );
2594
+ }
2595
+ const { url, token } = await authRes.json();
2596
+ openBrowser(url);
2597
+ process.stderr.write(
2598
+ ` ${ansi.cyanBright("Opening browser to authenticate...")}
2599
+
2600
+ ${ansi.gray("If the browser didn't open, visit:")}
2601
+ ${ansi.cyan(url)}
2602
+
2603
+ `
2604
+ );
2605
+ const POLL_INTERVAL = 2e3;
2606
+ const MAX_ATTEMPTS = 60;
2607
+ for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
2608
+ await sleep3(POLL_INTERVAL);
2609
+ const frame = SPINNER_FRAMES[attempt % SPINNER_FRAMES.length];
2610
+ const remaining = Math.ceil(
2611
+ MAX_ATTEMPTS * POLL_INTERVAL / 1e3 - (attempt + 1) * POLL_INTERVAL / 1e3
2612
+ );
2613
+ process.stderr.write(
2614
+ `\r ${ansi.cyan(frame)} Waiting for browser authorization... ${ansi.gray(`(${remaining}s)`)}`
2615
+ );
2616
+ const pollRes = await fetch(`${baseUrl}/developer/v2/poll-auth-url`, {
2617
+ method: "POST",
2618
+ headers: {
2619
+ "Content-Type": "application/json",
2620
+ "User-Agent": "@mindstudio-ai/agent"
2621
+ },
2622
+ body: JSON.stringify({ token })
2623
+ });
2624
+ if (!pollRes.ok) {
2625
+ process.stderr.write("\n");
2626
+ fatal(
2627
+ `Poll request failed: ${pollRes.status} ${pollRes.statusText}`
2628
+ );
2629
+ }
2630
+ const result = await pollRes.json();
2631
+ if (result.status === "completed" && result.apiKey) {
2632
+ process.stderr.write("\r\x1B[K");
2633
+ const { saveConfig: saveConfig2, getConfigPath: getConfigPath2 } = await Promise.resolve().then(() => (init_config(), config_exports));
2634
+ const config = {
2635
+ apiKey: result.apiKey
2636
+ };
2637
+ if (baseUrl !== DEFAULT_BASE_URL2) {
2638
+ config.baseUrl = baseUrl;
2639
+ }
2640
+ saveConfig2(config);
2641
+ process.stderr.write(
2642
+ ` ${ansi.greenBold("\u2714")} Authenticated successfully!
2643
+ ${ansi.gray("Credentials saved to")} ${getConfigPath2()}
2644
+
2645
+ `
2646
+ );
2647
+ return;
2648
+ }
2649
+ if (result.status === "expired") {
2650
+ process.stderr.write("\r\x1B[K");
2651
+ fatal("Authorization expired. Please try again.");
2652
+ }
2653
+ }
2654
+ process.stderr.write("\r\x1B[K");
2655
+ fatal("Authorization timed out. Please try again.");
2656
+ }
2657
+ async function cmdLogout() {
2658
+ const { loadConfig: loadConfig2, clearConfig: clearConfig2, getConfigPath: getConfigPath2 } = await Promise.resolve().then(() => (init_config(), config_exports));
2659
+ const config = loadConfig2();
2660
+ if (!config.apiKey) {
2661
+ process.stderr.write(
2662
+ ` ${ansi.gray("Not currently logged in.")}
2663
+ `
2664
+ );
2665
+ return;
2666
+ }
2667
+ clearConfig2();
2668
+ process.stderr.write(
2669
+ ` ${ansi.greenBold("\u2714")} Logged out. Credentials removed from ${ansi.gray(getConfigPath2())}
2670
+ `
2671
+ );
2672
+ }
2673
+ async function cmdWhoami(options) {
2674
+ let source;
2675
+ let detail = [];
2676
+ if (options.apiKey) {
2677
+ source = `${ansi.bold("--api-key flag")} ${ansi.gray("(CLI argument)")}`;
2678
+ } else if (process.env.MINDSTUDIO_API_KEY) {
2679
+ source = `${ansi.bold("MINDSTUDIO_API_KEY")} ${ansi.gray("(environment variable)")}`;
2680
+ detail.push(
2681
+ ` ${ansi.gray("Key:")} ${maskKey(process.env.MINDSTUDIO_API_KEY)}`
2682
+ );
2683
+ } else {
2684
+ const { loadConfig: loadConfig2, getConfigPath: getConfigPath2 } = await Promise.resolve().then(() => (init_config(), config_exports));
2685
+ const config = loadConfig2();
2686
+ if (config.apiKey) {
2687
+ source = `${ansi.bold("config file")} ${ansi.gray("(mindstudio login)")}`;
2688
+ detail.push(` ${ansi.gray("File:")} ${getConfigPath2()}`);
2689
+ detail.push(` ${ansi.gray("Key:")} ${maskKey(config.apiKey)}`);
2690
+ if (config.baseUrl) {
2691
+ detail.push(` ${ansi.gray("URL:")} ${config.baseUrl}`);
2692
+ }
2693
+ } else if (process.env.CALLBACK_TOKEN) {
2694
+ source = `${ansi.bold("CALLBACK_TOKEN")} ${ansi.gray("(managed/internal mode)")}`;
2695
+ } else {
2696
+ process.stderr.write(
2697
+ ` ${ansi.gray("\u25CB")} Not authenticated. Run ${ansi.cyan("mindstudio login")} to get started.
2698
+ `
2699
+ );
2700
+ return;
2701
+ }
2702
+ }
2703
+ process.stderr.write(` ${ansi.gray("Auth:")} ${source}
2704
+ `);
2705
+ for (const line of detail) process.stderr.write(line + "\n");
2706
+ process.stderr.write(` ${ansi.gray("Verifying...")} `);
2707
+ try {
2708
+ const { MindStudioAgent: MindStudioAgent2 } = await Promise.resolve().then(() => (init_client(), client_exports));
2709
+ const agent = new MindStudioAgent2({
2710
+ apiKey: options.apiKey,
2711
+ baseUrl: options.baseUrl
2712
+ });
2713
+ const result = await agent.listAgents();
2714
+ process.stderr.write(
2715
+ `\r\x1B[K ${ansi.greenBold("\u25CF")} ${ansi.green("Connected")} ${ansi.gray("\u2014")} ${result.orgName} ${ansi.gray("(" + result.orgId + ")")}
2716
+ `
2717
+ );
2718
+ } catch (err) {
2719
+ const message = err instanceof Error ? err.message : String(err);
2720
+ process.stderr.write(
2721
+ `\r\x1B[K ${ansi.dim("\u25CF")} ${ansi.dim("Not connected")} ${ansi.gray("\u2014")} ${message}
2722
+ `
2723
+ );
2724
+ }
2725
+ }
2361
2726
  function parseStepFlags(argv) {
2362
2727
  const result = {};
2363
2728
  for (let i = 0; i < argv.length; i++) {
@@ -2431,7 +2796,25 @@ async function main() {
2431
2796
  process.exit(positionals.length === 0 ? 1 : 0);
2432
2797
  }
2433
2798
  const command = positionals[0];
2799
+ const updatePromise = command !== "mcp" && command !== "login" ? checkForUpdate() : Promise.resolve(null);
2434
2800
  try {
2801
+ if (command === "login") {
2802
+ await cmdLogin({
2803
+ baseUrl: values["base-url"]
2804
+ });
2805
+ return;
2806
+ }
2807
+ if (command === "logout") {
2808
+ await cmdLogout();
2809
+ return;
2810
+ }
2811
+ if (command === "whoami") {
2812
+ await cmdWhoami({
2813
+ apiKey: values["api-key"],
2814
+ baseUrl: values["base-url"]
2815
+ });
2816
+ return;
2817
+ }
2435
2818
  if (command === "list") {
2436
2819
  await cmdList(values.json);
2437
2820
  return;
@@ -2551,6 +2934,9 @@ async function main() {
2551
2934
  } catch (err) {
2552
2935
  const message = err instanceof Error ? err.message : String(err);
2553
2936
  fatal(message);
2937
+ } finally {
2938
+ const latestVersion = await updatePromise;
2939
+ if (latestVersion) printUpdateNotice(latestVersion);
2554
2940
  }
2555
2941
  }
2556
2942
  main();