@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 +24 -11
- package/dist/cli.js +396 -10
- package/dist/index.d.ts +88 -9
- package/dist/index.js +50 -10
- package/dist/index.js.map +1 -1
- package/llms.txt +63 -10
- package/package.json +1 -1
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
|
-
#
|
|
50
|
-
|
|
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
|
|
99
|
+
The fastest way to authenticate is the interactive login:
|
|
100
100
|
|
|
101
|
-
|
|
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
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
|
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": "
|
|
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.
|
|
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
|
|
1659
|
-
const
|
|
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.
|
|
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();
|