@mindstudio-ai/agent 0.1.33 → 0.1.35
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/dist/cli.js +151 -33
- package/dist/index.d.ts +40 -1
- package/dist/index.js +1265 -1150
- package/dist/index.js.map +1 -1
- package/dist/postinstall.js +151 -33
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -37,13 +37,23 @@ async function requestWithRetry(config, method, url, body, attempt) {
|
|
|
37
37
|
return requestWithRetry(config, method, url, body, attempt + 1);
|
|
38
38
|
}
|
|
39
39
|
if (!res.ok) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
res.
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
let message = `${res.status} ${res.statusText}`;
|
|
41
|
+
let code = "api_error";
|
|
42
|
+
let details;
|
|
43
|
+
try {
|
|
44
|
+
const text = await res.text();
|
|
45
|
+
try {
|
|
46
|
+
const body2 = JSON.parse(text);
|
|
47
|
+
details = body2;
|
|
48
|
+
const errMsg = body2.error ?? body2.message ?? body2.details;
|
|
49
|
+
if (errMsg) message = errMsg;
|
|
50
|
+
if (body2.code) code = body2.code;
|
|
51
|
+
} catch {
|
|
52
|
+
if (text && text.length < 500) message = text;
|
|
53
|
+
}
|
|
54
|
+
} catch {
|
|
55
|
+
}
|
|
56
|
+
throw new MindStudioError(message, code, res.status, details);
|
|
47
57
|
}
|
|
48
58
|
const data = await res.json();
|
|
49
59
|
return { data, headers: res.headers };
|
|
@@ -1248,6 +1258,9 @@ var Table = class {
|
|
|
1248
1258
|
const items = (isArray ? data : [data]).map(
|
|
1249
1259
|
(item) => this._config.defaults ? { ...this._config.defaults, ...item } : item
|
|
1250
1260
|
);
|
|
1261
|
+
for (const item of items) {
|
|
1262
|
+
this._checkManagedColumns(item);
|
|
1263
|
+
}
|
|
1251
1264
|
const queries = items.map(
|
|
1252
1265
|
(item) => buildInsert(
|
|
1253
1266
|
this._config.tableName,
|
|
@@ -1265,7 +1278,13 @@ var Table = class {
|
|
|
1265
1278
|
}
|
|
1266
1279
|
return void 0;
|
|
1267
1280
|
});
|
|
1268
|
-
|
|
1281
|
+
const result = isArray ? rows : rows[0];
|
|
1282
|
+
this._syncRolesIfNeeded(
|
|
1283
|
+
items,
|
|
1284
|
+
result,
|
|
1285
|
+
isArray
|
|
1286
|
+
);
|
|
1287
|
+
return result;
|
|
1269
1288
|
});
|
|
1270
1289
|
}
|
|
1271
1290
|
/**
|
|
@@ -1273,20 +1292,25 @@ var Table = class {
|
|
|
1273
1292
|
* Returns the updated row via `UPDATE ... RETURNING *`.
|
|
1274
1293
|
*/
|
|
1275
1294
|
update(id, data) {
|
|
1295
|
+
this._checkManagedColumns(data);
|
|
1276
1296
|
const query = buildUpdate(
|
|
1277
1297
|
this._config.tableName,
|
|
1278
1298
|
id,
|
|
1279
1299
|
data,
|
|
1280
1300
|
this._config.columns
|
|
1281
1301
|
);
|
|
1282
|
-
return new Mutation(
|
|
1283
|
-
|
|
1284
|
-
[query],
|
|
1285
|
-
(results) => deserializeRow(
|
|
1302
|
+
return new Mutation(this._config, [query], (results) => {
|
|
1303
|
+
const result = deserializeRow(
|
|
1286
1304
|
results[0].rows[0],
|
|
1287
1305
|
this._config.columns
|
|
1288
|
-
)
|
|
1289
|
-
|
|
1306
|
+
);
|
|
1307
|
+
this._syncRolesIfNeeded(
|
|
1308
|
+
[data],
|
|
1309
|
+
result,
|
|
1310
|
+
false
|
|
1311
|
+
);
|
|
1312
|
+
return result;
|
|
1313
|
+
});
|
|
1290
1314
|
}
|
|
1291
1315
|
remove(id) {
|
|
1292
1316
|
const query = buildDelete(this._config.tableName, `id = ?`, [id]);
|
|
@@ -1341,24 +1365,65 @@ var Table = class {
|
|
|
1341
1365
|
const conflictColumns = Array.isArray(conflictKey) ? conflictKey : [conflictKey];
|
|
1342
1366
|
this._validateUniqueConstraint(conflictColumns);
|
|
1343
1367
|
const withDefaults = this._config.defaults ? { ...this._config.defaults, ...data } : data;
|
|
1368
|
+
this._checkManagedColumns(withDefaults);
|
|
1344
1369
|
const query = buildUpsert(
|
|
1345
1370
|
this._config.tableName,
|
|
1346
1371
|
withDefaults,
|
|
1347
1372
|
conflictColumns,
|
|
1348
1373
|
this._config.columns
|
|
1349
1374
|
);
|
|
1350
|
-
return new Mutation(
|
|
1351
|
-
|
|
1352
|
-
[query],
|
|
1353
|
-
(results) => deserializeRow(
|
|
1375
|
+
return new Mutation(this._config, [query], (results) => {
|
|
1376
|
+
const result = deserializeRow(
|
|
1354
1377
|
results[0].rows[0],
|
|
1355
1378
|
this._config.columns
|
|
1356
|
-
)
|
|
1357
|
-
|
|
1379
|
+
);
|
|
1380
|
+
this._syncRolesIfNeeded([withDefaults], result, false);
|
|
1381
|
+
return result;
|
|
1382
|
+
});
|
|
1358
1383
|
}
|
|
1359
1384
|
// -------------------------------------------------------------------------
|
|
1360
1385
|
// Internal helpers
|
|
1361
1386
|
// -------------------------------------------------------------------------
|
|
1387
|
+
/** @internal Throw if data includes a platform-managed email/phone column. */
|
|
1388
|
+
_checkManagedColumns(data) {
|
|
1389
|
+
const mc = this._config.managedColumns;
|
|
1390
|
+
if (!mc) return;
|
|
1391
|
+
const keys = Object.keys(data);
|
|
1392
|
+
for (const key of keys) {
|
|
1393
|
+
if (mc.email && key === mc.email || mc.phone && key === mc.phone) {
|
|
1394
|
+
throw new MindStudioError(
|
|
1395
|
+
`Cannot write to "${key}" \u2014 this column is managed by auth. Use the auth API to change a user's ${key === mc.email ? "email" : "phone"}.`,
|
|
1396
|
+
"managed_column_write",
|
|
1397
|
+
400
|
|
1398
|
+
);
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
/**
|
|
1403
|
+
* @internal Fire role sync for rows that wrote to the roles column.
|
|
1404
|
+
* Called inside processResult (runs after SQL execution in both
|
|
1405
|
+
* standalone and batch paths). Fire-and-forget.
|
|
1406
|
+
*/
|
|
1407
|
+
_syncRolesIfNeeded(inputItems, result, isArray) {
|
|
1408
|
+
const rolesCol = this._config.managedColumns?.roles;
|
|
1409
|
+
const syncRoles = this._config.syncRoles;
|
|
1410
|
+
if (!rolesCol || !syncRoles) return;
|
|
1411
|
+
if (!inputItems.some((item) => rolesCol in item)) return;
|
|
1412
|
+
if (isArray) {
|
|
1413
|
+
for (const row of result) {
|
|
1414
|
+
if (row?.id) {
|
|
1415
|
+
syncRoles(row.id, row[rolesCol]).catch(() => {
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
} else {
|
|
1420
|
+
const row = result;
|
|
1421
|
+
if (row?.id) {
|
|
1422
|
+
syncRoles(row.id, row[rolesCol]).catch(() => {
|
|
1423
|
+
});
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1362
1427
|
/** @internal Validate that the given columns match a declared unique constraint. */
|
|
1363
1428
|
_validateUniqueConstraint(columns) {
|
|
1364
1429
|
if (!this._config.unique?.length) {
|
|
@@ -1383,7 +1448,7 @@ var Table = class {
|
|
|
1383
1448
|
};
|
|
1384
1449
|
|
|
1385
1450
|
// src/db/index.ts
|
|
1386
|
-
function createDb(databases, executeBatch) {
|
|
1451
|
+
function createDb(databases, executeBatch, authConfig, syncRoles) {
|
|
1387
1452
|
return {
|
|
1388
1453
|
defineTable(name, options) {
|
|
1389
1454
|
const resolved = resolveTable(databases, name, options?.database);
|
|
@@ -1393,6 +1458,8 @@ function createDb(databases, executeBatch) {
|
|
|
1393
1458
|
columns: resolved.columns,
|
|
1394
1459
|
unique: options?.unique,
|
|
1395
1460
|
defaults: options?.defaults,
|
|
1461
|
+
managedColumns: authConfig?.table === name ? authConfig.columns : void 0,
|
|
1462
|
+
syncRoles: authConfig?.table === name && authConfig.columns.roles ? syncRoles : void 0,
|
|
1396
1463
|
executeBatch: (queries) => executeBatch(resolved.databaseId, queries)
|
|
1397
1464
|
};
|
|
1398
1465
|
return new Table(config);
|
|
@@ -1999,1135 +2066,6 @@ function applyStepMethods(AgentClass) {
|
|
|
1999
2066
|
};
|
|
2000
2067
|
}
|
|
2001
2068
|
|
|
2002
|
-
// src/client.ts
|
|
2003
|
-
var DEFAULT_BASE_URL = "https://v1.mindstudio-api.com";
|
|
2004
|
-
var DEFAULT_MAX_RETRIES = 3;
|
|
2005
|
-
var MindStudioAgent = class {
|
|
2006
|
-
/** @internal */
|
|
2007
|
-
_httpConfig;
|
|
2008
|
-
/** @internal */
|
|
2009
|
-
_reuseThreadId;
|
|
2010
|
-
/** @internal */
|
|
2011
|
-
_threadId;
|
|
2012
|
-
/** @internal Stream ID for SSE token streaming. Set by sandbox via STREAM_ID env var. */
|
|
2013
|
-
_streamId;
|
|
2014
|
-
// ---- App context (db + auth) ----
|
|
2015
|
-
/**
|
|
2016
|
-
* @internal App ID for context resolution. Resolved from:
|
|
2017
|
-
* constructor appId → MINDSTUDIO_APP_ID env → sandbox globals →
|
|
2018
|
-
* auto-detected from first executeStep response header.
|
|
2019
|
-
*/
|
|
2020
|
-
_appId;
|
|
2021
|
-
/**
|
|
2022
|
-
* @internal Cached app context (auth + databases). Populated by
|
|
2023
|
-
* ensureContext() and cached for the lifetime of the instance.
|
|
2024
|
-
*/
|
|
2025
|
-
_context;
|
|
2026
|
-
/**
|
|
2027
|
-
* @internal Deduplication promise for ensureContext(). Ensures only one
|
|
2028
|
-
* context fetch is in-flight at a time, even if multiple db/auth
|
|
2029
|
-
* operations trigger it concurrently.
|
|
2030
|
-
*/
|
|
2031
|
-
_contextPromise;
|
|
2032
|
-
/** @internal Cached AuthContext instance, created during context hydration. */
|
|
2033
|
-
_auth;
|
|
2034
|
-
/** @internal Cached Db namespace instance, created during context hydration. */
|
|
2035
|
-
_db;
|
|
2036
|
-
/** @internal Auth type — 'internal' for CALLBACK_TOKEN (managed mode), 'apiKey' otherwise. */
|
|
2037
|
-
_authType;
|
|
2038
|
-
/**
|
|
2039
|
-
* @internal Resolve the current auth token. For internal (CALLBACK_TOKEN)
|
|
2040
|
-
* auth, re-reads the env var each time so that long-lived singleton
|
|
2041
|
-
* instances pick up token rotations from the host process.
|
|
2042
|
-
*/
|
|
2043
|
-
get _token() {
|
|
2044
|
-
if (this._authType === "internal" && process.env.CALLBACK_TOKEN) {
|
|
2045
|
-
return process.env.CALLBACK_TOKEN;
|
|
2046
|
-
}
|
|
2047
|
-
return this._httpConfig.token;
|
|
2048
|
-
}
|
|
2049
|
-
constructor(options = {}) {
|
|
2050
|
-
const config = loadConfig();
|
|
2051
|
-
const { token, authType } = resolveToken(options.apiKey, config);
|
|
2052
|
-
const baseUrl = options.baseUrl ?? process.env.MINDSTUDIO_BASE_URL ?? process.env.REMOTE_HOSTNAME ?? config.baseUrl ?? DEFAULT_BASE_URL;
|
|
2053
|
-
this._reuseThreadId = options.reuseThreadId ?? /^(true|1)$/i.test(process.env.MINDSTUDIO_REUSE_THREAD_ID ?? "");
|
|
2054
|
-
this._appId = options.appId ?? process.env.MINDSTUDIO_APP_ID ?? void 0;
|
|
2055
|
-
this._authType = authType;
|
|
2056
|
-
this._httpConfig = {
|
|
2057
|
-
baseUrl,
|
|
2058
|
-
token,
|
|
2059
|
-
rateLimiter: new RateLimiter(authType),
|
|
2060
|
-
maxRetries: options.maxRetries ?? DEFAULT_MAX_RETRIES
|
|
2061
|
-
};
|
|
2062
|
-
if (authType === "internal") {
|
|
2063
|
-
this._trySandboxHydration();
|
|
2064
|
-
}
|
|
2065
|
-
this._streamId = process.env.STREAM_ID ?? void 0;
|
|
2066
|
-
}
|
|
2067
|
-
/**
|
|
2068
|
-
* Execute any step by its type name. This is the low-level method that all
|
|
2069
|
-
* typed step methods delegate to. Use it as an escape hatch for step types
|
|
2070
|
-
* not yet covered by the generated methods.
|
|
2071
|
-
*
|
|
2072
|
-
* ```ts
|
|
2073
|
-
* const result = await agent.executeStep("generateImage", { prompt: "hello", mode: "background" });
|
|
2074
|
-
* ```
|
|
2075
|
-
*/
|
|
2076
|
-
async executeStep(stepType, step, options) {
|
|
2077
|
-
if (options?.onLog) {
|
|
2078
|
-
return this._executeStepStreaming(
|
|
2079
|
-
stepType,
|
|
2080
|
-
step,
|
|
2081
|
-
options
|
|
2082
|
-
);
|
|
2083
|
-
}
|
|
2084
|
-
const threadId = options?.threadId ?? (this._reuseThreadId ? this._threadId : void 0);
|
|
2085
|
-
const { data, headers } = await request(this._httpConfig, "POST", `/steps/${stepType}/execute`, {
|
|
2086
|
-
step,
|
|
2087
|
-
...options?.appId != null && { appId: options.appId },
|
|
2088
|
-
...threadId != null && { threadId },
|
|
2089
|
-
...this._streamId != null && { streamId: this._streamId }
|
|
2090
|
-
});
|
|
2091
|
-
let output;
|
|
2092
|
-
if (data.output != null) {
|
|
2093
|
-
output = data.output;
|
|
2094
|
-
} else if (data.outputUrl) {
|
|
2095
|
-
const res = await fetch(data.outputUrl);
|
|
2096
|
-
if (!res.ok) {
|
|
2097
|
-
throw new MindStudioError(
|
|
2098
|
-
`Failed to fetch output from S3: ${res.status} ${res.statusText}`,
|
|
2099
|
-
"output_fetch_error",
|
|
2100
|
-
res.status
|
|
2101
|
-
);
|
|
2102
|
-
}
|
|
2103
|
-
const envelope = await res.json();
|
|
2104
|
-
output = envelope.value;
|
|
2105
|
-
} else {
|
|
2106
|
-
output = void 0;
|
|
2107
|
-
}
|
|
2108
|
-
const returnedThreadId = headers.get("x-mindstudio-thread-id") ?? "";
|
|
2109
|
-
if (this._reuseThreadId && returnedThreadId) {
|
|
2110
|
-
this._threadId = returnedThreadId;
|
|
2111
|
-
}
|
|
2112
|
-
const returnedAppId = headers.get("x-mindstudio-app-id");
|
|
2113
|
-
if (!this._appId && returnedAppId) {
|
|
2114
|
-
this._appId = returnedAppId;
|
|
2115
|
-
}
|
|
2116
|
-
const remaining = headers.get("x-ratelimit-remaining");
|
|
2117
|
-
const billingCost = headers.get("x-mindstudio-billing-cost");
|
|
2118
|
-
const billingEvents = headers.get("x-mindstudio-billing-events");
|
|
2119
|
-
return {
|
|
2120
|
-
...output,
|
|
2121
|
-
$appId: headers.get("x-mindstudio-app-id") ?? "",
|
|
2122
|
-
$threadId: returnedThreadId,
|
|
2123
|
-
$rateLimitRemaining: remaining != null ? parseInt(remaining, 10) : void 0,
|
|
2124
|
-
$billingCost: billingCost != null ? parseFloat(billingCost) : void 0,
|
|
2125
|
-
$billingEvents: billingEvents != null ? JSON.parse(billingEvents) : void 0
|
|
2126
|
-
};
|
|
2127
|
-
}
|
|
2128
|
-
/**
|
|
2129
|
-
* @internal Streaming step execution — sends `Accept: text/event-stream`
|
|
2130
|
-
* and parses SSE events for real-time debug logs.
|
|
2131
|
-
*/
|
|
2132
|
-
async _executeStepStreaming(stepType, step, options) {
|
|
2133
|
-
const threadId = options.threadId ?? (this._reuseThreadId ? this._threadId : void 0);
|
|
2134
|
-
const url = `${this._httpConfig.baseUrl}/developer/v2/steps/${stepType}/execute`;
|
|
2135
|
-
const body = {
|
|
2136
|
-
step,
|
|
2137
|
-
...options.appId != null && { appId: options.appId },
|
|
2138
|
-
...threadId != null && { threadId },
|
|
2139
|
-
...this._streamId != null && { streamId: this._streamId }
|
|
2140
|
-
};
|
|
2141
|
-
await this._httpConfig.rateLimiter.acquire();
|
|
2142
|
-
let res;
|
|
2143
|
-
try {
|
|
2144
|
-
res = await fetch(url, {
|
|
2145
|
-
method: "POST",
|
|
2146
|
-
headers: {
|
|
2147
|
-
Authorization: `Bearer ${this._token}`,
|
|
2148
|
-
"Content-Type": "application/json",
|
|
2149
|
-
"User-Agent": "@mindstudio-ai/agent",
|
|
2150
|
-
Accept: "text/event-stream"
|
|
2151
|
-
},
|
|
2152
|
-
body: JSON.stringify(body)
|
|
2153
|
-
});
|
|
2154
|
-
} catch (err) {
|
|
2155
|
-
this._httpConfig.rateLimiter.release();
|
|
2156
|
-
throw err;
|
|
2157
|
-
}
|
|
2158
|
-
this._httpConfig.rateLimiter.updateFromHeaders(res.headers);
|
|
2159
|
-
if (!res.ok) {
|
|
2160
|
-
this._httpConfig.rateLimiter.release();
|
|
2161
|
-
const errorBody = await res.json().catch(() => ({}));
|
|
2162
|
-
throw new MindStudioError(
|
|
2163
|
-
errorBody.message || `${res.status} ${res.statusText}`,
|
|
2164
|
-
errorBody.code || "api_error",
|
|
2165
|
-
res.status,
|
|
2166
|
-
errorBody
|
|
2167
|
-
);
|
|
2168
|
-
}
|
|
2169
|
-
const headers = res.headers;
|
|
2170
|
-
try {
|
|
2171
|
-
const reader = res.body.getReader();
|
|
2172
|
-
const decoder = new TextDecoder();
|
|
2173
|
-
let buffer = "";
|
|
2174
|
-
let doneEvent = null;
|
|
2175
|
-
while (true) {
|
|
2176
|
-
const { done, value } = await reader.read();
|
|
2177
|
-
if (done) break;
|
|
2178
|
-
buffer += decoder.decode(value, { stream: true });
|
|
2179
|
-
const lines = buffer.split("\n");
|
|
2180
|
-
buffer = lines.pop() ?? "";
|
|
2181
|
-
for (const line of lines) {
|
|
2182
|
-
if (!line.startsWith("data: ")) continue;
|
|
2183
|
-
try {
|
|
2184
|
-
const event = JSON.parse(line.slice(6));
|
|
2185
|
-
if (event.type === "log") {
|
|
2186
|
-
options.onLog({
|
|
2187
|
-
value: event.value,
|
|
2188
|
-
tag: event.tag,
|
|
2189
|
-
ts: event.ts
|
|
2190
|
-
});
|
|
2191
|
-
} else if (event.type === "done") {
|
|
2192
|
-
doneEvent = {
|
|
2193
|
-
output: event.output,
|
|
2194
|
-
outputUrl: event.outputUrl,
|
|
2195
|
-
billingCost: event.billingCost,
|
|
2196
|
-
billingEvents: event.billingEvents
|
|
2197
|
-
};
|
|
2198
|
-
} else if (event.type === "error") {
|
|
2199
|
-
throw new MindStudioError(
|
|
2200
|
-
event.error || "Step execution failed",
|
|
2201
|
-
"step_error",
|
|
2202
|
-
500
|
|
2203
|
-
);
|
|
2204
|
-
}
|
|
2205
|
-
} catch (err) {
|
|
2206
|
-
if (err instanceof MindStudioError) throw err;
|
|
2207
|
-
}
|
|
2208
|
-
}
|
|
2209
|
-
}
|
|
2210
|
-
if (buffer.startsWith("data: ")) {
|
|
2211
|
-
try {
|
|
2212
|
-
const event = JSON.parse(buffer.slice(6));
|
|
2213
|
-
if (event.type === "done") {
|
|
2214
|
-
doneEvent = {
|
|
2215
|
-
output: event.output,
|
|
2216
|
-
outputUrl: event.outputUrl,
|
|
2217
|
-
billingCost: event.billingCost,
|
|
2218
|
-
billingEvents: event.billingEvents
|
|
2219
|
-
};
|
|
2220
|
-
} else if (event.type === "error") {
|
|
2221
|
-
throw new MindStudioError(
|
|
2222
|
-
event.error || "Step execution failed",
|
|
2223
|
-
"step_error",
|
|
2224
|
-
500
|
|
2225
|
-
);
|
|
2226
|
-
} else if (event.type === "log") {
|
|
2227
|
-
options.onLog({
|
|
2228
|
-
value: event.value,
|
|
2229
|
-
tag: event.tag,
|
|
2230
|
-
ts: event.ts
|
|
2231
|
-
});
|
|
2232
|
-
}
|
|
2233
|
-
} catch (err) {
|
|
2234
|
-
if (err instanceof MindStudioError) throw err;
|
|
2235
|
-
}
|
|
2236
|
-
}
|
|
2237
|
-
if (!doneEvent) {
|
|
2238
|
-
throw new MindStudioError(
|
|
2239
|
-
"Stream ended without a done event",
|
|
2240
|
-
"stream_error",
|
|
2241
|
-
500
|
|
2242
|
-
);
|
|
2243
|
-
}
|
|
2244
|
-
let output;
|
|
2245
|
-
if (doneEvent.output != null) {
|
|
2246
|
-
output = doneEvent.output;
|
|
2247
|
-
} else if (doneEvent.outputUrl) {
|
|
2248
|
-
const s3Res = await fetch(doneEvent.outputUrl);
|
|
2249
|
-
if (!s3Res.ok) {
|
|
2250
|
-
throw new MindStudioError(
|
|
2251
|
-
`Failed to fetch output from S3: ${s3Res.status} ${s3Res.statusText}`,
|
|
2252
|
-
"output_fetch_error",
|
|
2253
|
-
s3Res.status
|
|
2254
|
-
);
|
|
2255
|
-
}
|
|
2256
|
-
const envelope = await s3Res.json();
|
|
2257
|
-
output = envelope.value;
|
|
2258
|
-
} else {
|
|
2259
|
-
output = void 0;
|
|
2260
|
-
}
|
|
2261
|
-
const returnedThreadId = headers.get("x-mindstudio-thread-id") ?? "";
|
|
2262
|
-
if (this._reuseThreadId && returnedThreadId) {
|
|
2263
|
-
this._threadId = returnedThreadId;
|
|
2264
|
-
}
|
|
2265
|
-
const returnedAppId = headers.get("x-mindstudio-app-id");
|
|
2266
|
-
if (!this._appId && returnedAppId) {
|
|
2267
|
-
this._appId = returnedAppId;
|
|
2268
|
-
}
|
|
2269
|
-
const remaining = headers.get("x-ratelimit-remaining");
|
|
2270
|
-
return {
|
|
2271
|
-
...output,
|
|
2272
|
-
$appId: headers.get("x-mindstudio-app-id") ?? "",
|
|
2273
|
-
$threadId: returnedThreadId,
|
|
2274
|
-
$rateLimitRemaining: remaining != null ? parseInt(remaining, 10) : void 0,
|
|
2275
|
-
$billingCost: doneEvent.billingCost,
|
|
2276
|
-
$billingEvents: doneEvent.billingEvents
|
|
2277
|
-
};
|
|
2278
|
-
} finally {
|
|
2279
|
-
this._httpConfig.rateLimiter.release();
|
|
2280
|
-
}
|
|
2281
|
-
}
|
|
2282
|
-
/**
|
|
2283
|
-
* Execute multiple steps in parallel in a single request.
|
|
2284
|
-
*
|
|
2285
|
-
* All steps run in parallel on the server. Results are returned in the same
|
|
2286
|
-
* order as the input. Individual step failures do not affect other steps —
|
|
2287
|
-
* partial success is possible.
|
|
2288
|
-
*
|
|
2289
|
-
* ```ts
|
|
2290
|
-
* const { results } = await agent.executeStepBatch([
|
|
2291
|
-
* { stepType: 'generateImage', step: { prompt: 'a sunset' } },
|
|
2292
|
-
* { stepType: 'textToSpeech', step: { text: 'Hello world' } },
|
|
2293
|
-
* ]);
|
|
2294
|
-
* ```
|
|
2295
|
-
*/
|
|
2296
|
-
async executeStepBatch(steps, options) {
|
|
2297
|
-
const threadId = options?.threadId ?? (this._reuseThreadId ? this._threadId : void 0);
|
|
2298
|
-
const { data } = await request(this._httpConfig, "POST", "/steps/execute-batch", {
|
|
2299
|
-
steps,
|
|
2300
|
-
...options?.appId != null && { appId: options.appId },
|
|
2301
|
-
...threadId != null && { threadId }
|
|
2302
|
-
});
|
|
2303
|
-
const results = await Promise.all(
|
|
2304
|
-
data.results.map(async (r) => {
|
|
2305
|
-
if (r.output != null) {
|
|
2306
|
-
return {
|
|
2307
|
-
stepType: r.stepType,
|
|
2308
|
-
output: r.output,
|
|
2309
|
-
billingCost: r.billingCost,
|
|
2310
|
-
error: r.error
|
|
2311
|
-
};
|
|
2312
|
-
}
|
|
2313
|
-
if (r.outputUrl) {
|
|
2314
|
-
const res = await fetch(r.outputUrl);
|
|
2315
|
-
if (!res.ok) {
|
|
2316
|
-
return {
|
|
2317
|
-
stepType: r.stepType,
|
|
2318
|
-
error: `Failed to fetch output from S3: ${res.status} ${res.statusText}`
|
|
2319
|
-
};
|
|
2320
|
-
}
|
|
2321
|
-
const envelope = await res.json();
|
|
2322
|
-
return {
|
|
2323
|
-
stepType: r.stepType,
|
|
2324
|
-
output: envelope.value,
|
|
2325
|
-
billingCost: r.billingCost
|
|
2326
|
-
};
|
|
2327
|
-
}
|
|
2328
|
-
return {
|
|
2329
|
-
stepType: r.stepType,
|
|
2330
|
-
billingCost: r.billingCost,
|
|
2331
|
-
error: r.error
|
|
2332
|
-
};
|
|
2333
|
-
})
|
|
2334
|
-
);
|
|
2335
|
-
if (this._reuseThreadId && data.threadId) {
|
|
2336
|
-
this._threadId = data.threadId;
|
|
2337
|
-
}
|
|
2338
|
-
return {
|
|
2339
|
-
results,
|
|
2340
|
-
totalBillingCost: data.totalBillingCost,
|
|
2341
|
-
appId: data.appId,
|
|
2342
|
-
threadId: data.threadId
|
|
2343
|
-
};
|
|
2344
|
-
}
|
|
2345
|
-
/**
|
|
2346
|
-
* Get the authenticated user's identity and organization info.
|
|
2347
|
-
*
|
|
2348
|
-
* ```ts
|
|
2349
|
-
* const info = await agent.getUserInfo();
|
|
2350
|
-
* console.log(info.displayName, info.organizationName);
|
|
2351
|
-
* ```
|
|
2352
|
-
*/
|
|
2353
|
-
async getUserInfo() {
|
|
2354
|
-
const { data } = await request(
|
|
2355
|
-
this._httpConfig,
|
|
2356
|
-
"GET",
|
|
2357
|
-
"/account/userinfo"
|
|
2358
|
-
);
|
|
2359
|
-
return data;
|
|
2360
|
-
}
|
|
2361
|
-
/**
|
|
2362
|
-
* List all pre-built agents in the organization.
|
|
2363
|
-
*
|
|
2364
|
-
* ```ts
|
|
2365
|
-
* const { apps } = await agent.listAgents();
|
|
2366
|
-
* for (const app of apps) console.log(app.name, app.id);
|
|
2367
|
-
* ```
|
|
2368
|
-
*/
|
|
2369
|
-
async listAgents() {
|
|
2370
|
-
const { data } = await request(
|
|
2371
|
-
this._httpConfig,
|
|
2372
|
-
"GET",
|
|
2373
|
-
"/agents/load"
|
|
2374
|
-
);
|
|
2375
|
-
return data;
|
|
2376
|
-
}
|
|
2377
|
-
/**
|
|
2378
|
-
* Run a pre-built agent and wait for the result.
|
|
2379
|
-
*
|
|
2380
|
-
* Uses async polling internally — the request returns immediately with a
|
|
2381
|
-
* callback token, then polls until the run completes or fails.
|
|
2382
|
-
*
|
|
2383
|
-
* ```ts
|
|
2384
|
-
* const result = await agent.runAgent({
|
|
2385
|
-
* appId: 'your-agent-id',
|
|
2386
|
-
* variables: { query: 'hello' },
|
|
2387
|
-
* });
|
|
2388
|
-
* console.log(result.result);
|
|
2389
|
-
* ```
|
|
2390
|
-
*/
|
|
2391
|
-
async runAgent(options) {
|
|
2392
|
-
const pollInterval = options.pollIntervalMs ?? 1e3;
|
|
2393
|
-
const { data } = await request(this._httpConfig, "POST", "/agents/run", {
|
|
2394
|
-
appId: options.appId,
|
|
2395
|
-
async: true,
|
|
2396
|
-
...options.variables != null && { variables: options.variables },
|
|
2397
|
-
...options.workflow != null && { workflow: options.workflow },
|
|
2398
|
-
...options.version != null && { version: options.version },
|
|
2399
|
-
...options.includeBillingCost != null && {
|
|
2400
|
-
includeBillingCost: options.includeBillingCost
|
|
2401
|
-
},
|
|
2402
|
-
...options.metadata != null && { metadata: options.metadata }
|
|
2403
|
-
});
|
|
2404
|
-
const token = data.callbackToken;
|
|
2405
|
-
const pollUrl = `${this._httpConfig.baseUrl}/developer/v2/agents/run/poll/${token}`;
|
|
2406
|
-
while (true) {
|
|
2407
|
-
await sleep2(pollInterval);
|
|
2408
|
-
const res = await fetch(pollUrl, {
|
|
2409
|
-
headers: { "User-Agent": "@mindstudio-ai/agent" }
|
|
2410
|
-
});
|
|
2411
|
-
if (res.status === 404) {
|
|
2412
|
-
throw new MindStudioError(
|
|
2413
|
-
"Poll token not found or expired.",
|
|
2414
|
-
"poll_token_expired",
|
|
2415
|
-
404
|
|
2416
|
-
);
|
|
2417
|
-
}
|
|
2418
|
-
if (!res.ok) {
|
|
2419
|
-
const errorBody = await res.json().catch(() => ({}));
|
|
2420
|
-
throw new MindStudioError(
|
|
2421
|
-
errorBody.message ?? errorBody.error ?? `Poll request failed: ${res.status} ${res.statusText}`,
|
|
2422
|
-
errorBody.code ?? "poll_error",
|
|
2423
|
-
res.status,
|
|
2424
|
-
errorBody
|
|
2425
|
-
);
|
|
2426
|
-
}
|
|
2427
|
-
const poll = await res.json();
|
|
2428
|
-
if (poll.status === "pending") continue;
|
|
2429
|
-
if (poll.status === "error") {
|
|
2430
|
-
throw new MindStudioError(
|
|
2431
|
-
poll.error ?? "Agent run failed.",
|
|
2432
|
-
"agent_run_error",
|
|
2433
|
-
500
|
|
2434
|
-
);
|
|
2435
|
-
}
|
|
2436
|
-
return poll.result;
|
|
2437
|
-
}
|
|
2438
|
-
}
|
|
2439
|
-
/** @internal Used by generated action methods. */
|
|
2440
|
-
_request(method, path, body) {
|
|
2441
|
-
return request(this._httpConfig, method, path, body);
|
|
2442
|
-
}
|
|
2443
|
-
// -------------------------------------------------------------------------
|
|
2444
|
-
// Helper methods — models
|
|
2445
|
-
// -------------------------------------------------------------------------
|
|
2446
|
-
/** List all available AI models. */
|
|
2447
|
-
async listModels() {
|
|
2448
|
-
const { data } = await request(
|
|
2449
|
-
this._httpConfig,
|
|
2450
|
-
"GET",
|
|
2451
|
-
"/helpers/models"
|
|
2452
|
-
);
|
|
2453
|
-
return data;
|
|
2454
|
-
}
|
|
2455
|
-
/** List AI models filtered by type. */
|
|
2456
|
-
async listModelsByType(modelType) {
|
|
2457
|
-
const { data } = await request(
|
|
2458
|
-
this._httpConfig,
|
|
2459
|
-
"GET",
|
|
2460
|
-
`/helpers/models/${modelType}`
|
|
2461
|
-
);
|
|
2462
|
-
return data;
|
|
2463
|
-
}
|
|
2464
|
-
/** List all available AI models (summary). Returns only id, name, type, and tags. */
|
|
2465
|
-
async listModelsSummary() {
|
|
2466
|
-
const { data } = await request(
|
|
2467
|
-
this._httpConfig,
|
|
2468
|
-
"GET",
|
|
2469
|
-
"/helpers/models-summary"
|
|
2470
|
-
);
|
|
2471
|
-
return data;
|
|
2472
|
-
}
|
|
2473
|
-
/** List AI models (summary) filtered by type. */
|
|
2474
|
-
async listModelsSummaryByType(modelType) {
|
|
2475
|
-
const { data } = await request(
|
|
2476
|
-
this._httpConfig,
|
|
2477
|
-
"GET",
|
|
2478
|
-
`/helpers/models-summary/${modelType}`
|
|
2479
|
-
);
|
|
2480
|
-
return data;
|
|
2481
|
-
}
|
|
2482
|
-
// -------------------------------------------------------------------------
|
|
2483
|
-
// Helper methods — OAuth connectors & connections
|
|
2484
|
-
// -------------------------------------------------------------------------
|
|
2485
|
-
/**
|
|
2486
|
-
* List available OAuth connector services (Slack, Google, HubSpot, etc.).
|
|
2487
|
-
*
|
|
2488
|
-
* These are third-party integrations from the MindStudio Connector Registry.
|
|
2489
|
-
* For most tasks, use actions directly instead.
|
|
2490
|
-
*/
|
|
2491
|
-
async listConnectors() {
|
|
2492
|
-
const { data } = await request(
|
|
2493
|
-
this._httpConfig,
|
|
2494
|
-
"GET",
|
|
2495
|
-
"/helpers/connectors"
|
|
2496
|
-
);
|
|
2497
|
-
return data;
|
|
2498
|
-
}
|
|
2499
|
-
/** Get details for a single OAuth connector service. */
|
|
2500
|
-
async getConnector(serviceId) {
|
|
2501
|
-
const { data } = await request(
|
|
2502
|
-
this._httpConfig,
|
|
2503
|
-
"GET",
|
|
2504
|
-
`/helpers/connectors/${serviceId}`
|
|
2505
|
-
);
|
|
2506
|
-
return data;
|
|
2507
|
-
}
|
|
2508
|
-
/** Get the full configuration for an OAuth connector action, including input fields. */
|
|
2509
|
-
async getConnectorAction(serviceId, actionId) {
|
|
2510
|
-
const { data } = await request(
|
|
2511
|
-
this._httpConfig,
|
|
2512
|
-
"GET",
|
|
2513
|
-
`/helpers/connectors/${serviceId}/${actionId}`
|
|
2514
|
-
);
|
|
2515
|
-
return data;
|
|
2516
|
-
}
|
|
2517
|
-
/** List OAuth connections for the organization. These are authenticated third-party service links. */
|
|
2518
|
-
async listConnections() {
|
|
2519
|
-
const { data } = await request(
|
|
2520
|
-
this._httpConfig,
|
|
2521
|
-
"GET",
|
|
2522
|
-
"/helpers/connections"
|
|
2523
|
-
);
|
|
2524
|
-
return data;
|
|
2525
|
-
}
|
|
2526
|
-
// -------------------------------------------------------------------------
|
|
2527
|
-
// Helper methods — cost estimation
|
|
2528
|
-
// -------------------------------------------------------------------------
|
|
2529
|
-
/** Estimate the cost of executing an action before running it. */
|
|
2530
|
-
async estimateStepCost(stepType, step, options) {
|
|
2531
|
-
const { data } = await request(this._httpConfig, "POST", "/helpers/step-cost-estimate", {
|
|
2532
|
-
step: { type: stepType, ...step },
|
|
2533
|
-
...options
|
|
2534
|
-
});
|
|
2535
|
-
return data;
|
|
2536
|
-
}
|
|
2537
|
-
// -------------------------------------------------------------------------
|
|
2538
|
-
// Streaming
|
|
2539
|
-
// -------------------------------------------------------------------------
|
|
2540
|
-
/**
|
|
2541
|
-
* Send a stream chunk to the caller via SSE.
|
|
2542
|
-
*
|
|
2543
|
-
* When invoked from a method that was called with `stream: true`, chunks
|
|
2544
|
-
* are delivered in real-time as Server-Sent Events. When there is no active
|
|
2545
|
-
* stream (no `STREAM_ID`), calls are silently ignored — so it's safe to
|
|
2546
|
-
* call unconditionally.
|
|
2547
|
-
*
|
|
2548
|
-
* Accepts strings (sent as `type: 'token'`) or structured data (sent as
|
|
2549
|
-
* `type: 'data'`). The caller receives each chunk as an SSE event.
|
|
2550
|
-
*
|
|
2551
|
-
* @example
|
|
2552
|
-
* ```ts
|
|
2553
|
-
* // Stream text tokens
|
|
2554
|
-
* await agent.stream('Processing item 1...');
|
|
2555
|
-
*
|
|
2556
|
-
* // Stream structured data
|
|
2557
|
-
* await agent.stream({ progress: 50, currentItem: 'abc' });
|
|
2558
|
-
* ```
|
|
2559
|
-
*/
|
|
2560
|
-
stream = async (data) => {
|
|
2561
|
-
if (!this._streamId) return;
|
|
2562
|
-
const url = `${this._httpConfig.baseUrl}/_internal/v2/stream-chunk`;
|
|
2563
|
-
const body = typeof data === "string" ? { streamId: this._streamId, type: "token", text: data } : { streamId: this._streamId, type: "data", data };
|
|
2564
|
-
const res = await fetch(url, {
|
|
2565
|
-
method: "POST",
|
|
2566
|
-
headers: {
|
|
2567
|
-
"Content-Type": "application/json",
|
|
2568
|
-
Authorization: this._token
|
|
2569
|
-
},
|
|
2570
|
-
body: JSON.stringify(body)
|
|
2571
|
-
});
|
|
2572
|
-
if (!res.ok) {
|
|
2573
|
-
const text = await res.text().catch(() => "");
|
|
2574
|
-
console.warn(`[mindstudio] stream chunk failed: ${res.status} ${text}`);
|
|
2575
|
-
}
|
|
2576
|
-
};
|
|
2577
|
-
// -------------------------------------------------------------------------
|
|
2578
|
-
// db + auth namespaces
|
|
2579
|
-
// -------------------------------------------------------------------------
|
|
2580
|
-
/**
|
|
2581
|
-
* The `auth` namespace — synchronous role-based access control.
|
|
2582
|
-
*
|
|
2583
|
-
* Provides the current user's identity and roles. All methods are
|
|
2584
|
-
* synchronous since the role map is preloaded during context hydration.
|
|
2585
|
-
*
|
|
2586
|
-
* **Important**: Context must be hydrated before accessing `auth`.
|
|
2587
|
-
* - Inside the MindStudio sandbox: automatic (populated from globals)
|
|
2588
|
-
* - Outside the sandbox: call `await agent.ensureContext()` first,
|
|
2589
|
-
* or access `auth` after any `db` operation (which auto-hydrates)
|
|
2590
|
-
*
|
|
2591
|
-
* @throws {MindStudioError} if context has not been hydrated yet
|
|
2592
|
-
*
|
|
2593
|
-
* @example
|
|
2594
|
-
* ```ts
|
|
2595
|
-
* await agent.ensureContext();
|
|
2596
|
-
* agent.auth.requireRole(Roles.admin);
|
|
2597
|
-
* const admins = agent.auth.getUsersByRole(Roles.admin);
|
|
2598
|
-
* ```
|
|
2599
|
-
*/
|
|
2600
|
-
get auth() {
|
|
2601
|
-
if (!this._auth) {
|
|
2602
|
-
this._trySandboxHydration();
|
|
2603
|
-
}
|
|
2604
|
-
if (!this._auth) {
|
|
2605
|
-
throw new MindStudioError(
|
|
2606
|
-
"Auth context not yet loaded. Call `await agent.ensureContext()` or perform any db operation first (which auto-hydrates context). Inside the MindStudio sandbox, context is loaded automatically.",
|
|
2607
|
-
"context_not_loaded",
|
|
2608
|
-
400
|
|
2609
|
-
);
|
|
2610
|
-
}
|
|
2611
|
-
return this._auth;
|
|
2612
|
-
}
|
|
2613
|
-
/**
|
|
2614
|
-
* The `db` namespace — chainable collection API over managed databases.
|
|
2615
|
-
*
|
|
2616
|
-
* Use `db.defineTable<T>(name)` to get a typed Table<T>, then call
|
|
2617
|
-
* collection methods (filter, sortBy, push, update, etc.) on it.
|
|
2618
|
-
*
|
|
2619
|
-
* Context is auto-hydrated on first query execution — you can safely
|
|
2620
|
-
* call `defineTable()` at module scope without triggering any HTTP.
|
|
2621
|
-
*
|
|
2622
|
-
* @example
|
|
2623
|
-
* ```ts
|
|
2624
|
-
* const Orders = agent.db.defineTable<Order>('orders');
|
|
2625
|
-
* const active = await Orders.filter(o => o.status === 'active').take(10);
|
|
2626
|
-
* ```
|
|
2627
|
-
*/
|
|
2628
|
-
get db() {
|
|
2629
|
-
if (!this._db) {
|
|
2630
|
-
this._trySandboxHydration();
|
|
2631
|
-
}
|
|
2632
|
-
if (this._db) return this._db;
|
|
2633
|
-
return this._createLazyDb();
|
|
2634
|
-
}
|
|
2635
|
-
/**
|
|
2636
|
-
* Hydrate the app context (auth + database metadata). This must be
|
|
2637
|
-
* called before using `auth` synchronously. For `db`, hydration happens
|
|
2638
|
-
* automatically on first query.
|
|
2639
|
-
*
|
|
2640
|
-
* Context is fetched once and cached for the instance's lifetime.
|
|
2641
|
-
* Calling `ensureContext()` multiple times is safe (no-op after first).
|
|
2642
|
-
*
|
|
2643
|
-
* Context sources (checked in order):
|
|
2644
|
-
* 1. Sandbox globals (`globalThis.ai.auth`, `globalThis.ai.databases`)
|
|
2645
|
-
* 2. HTTP: `GET /developer/v2/helpers/app-context?appId={appId}`
|
|
2646
|
-
*
|
|
2647
|
-
* @throws {MindStudioError} if no `appId` is available
|
|
2648
|
-
*
|
|
2649
|
-
* @example
|
|
2650
|
-
* ```ts
|
|
2651
|
-
* await agent.ensureContext();
|
|
2652
|
-
* // auth is now available synchronously
|
|
2653
|
-
* agent.auth.requireRole(Roles.admin);
|
|
2654
|
-
* ```
|
|
2655
|
-
*/
|
|
2656
|
-
async ensureContext() {
|
|
2657
|
-
if (this._context) return;
|
|
2658
|
-
if (!this._contextPromise) {
|
|
2659
|
-
this._contextPromise = this._hydrateContext();
|
|
2660
|
-
}
|
|
2661
|
-
await this._contextPromise;
|
|
2662
|
-
}
|
|
2663
|
-
/**
|
|
2664
|
-
* @internal Fetch and cache app context, then create auth + db instances.
|
|
2665
|
-
*
|
|
2666
|
-
* In managed mode (CALLBACK_TOKEN), the platform resolves the app from
|
|
2667
|
-
* the token — no appId needed. With an API key, appId is required.
|
|
2668
|
-
*/
|
|
2669
|
-
async _hydrateContext() {
|
|
2670
|
-
if (!this._appId && this._authType !== "internal") {
|
|
2671
|
-
throw new MindStudioError(
|
|
2672
|
-
"No app ID available for context resolution. Pass `appId` to the constructor, set the MINDSTUDIO_APP_ID environment variable, or make a step execution call first (which auto-detects the app ID).",
|
|
2673
|
-
"missing_app_id",
|
|
2674
|
-
400
|
|
2675
|
-
);
|
|
2676
|
-
}
|
|
2677
|
-
const context = await this.getAppContext(this._appId);
|
|
2678
|
-
this._applyContext(context);
|
|
2679
|
-
}
|
|
2680
|
-
/**
|
|
2681
|
-
* @internal Apply a resolved context object — creates AuthContext and Db.
|
|
2682
|
-
* Used by both the HTTP path and sandbox hydration.
|
|
2683
|
-
*/
|
|
2684
|
-
_applyContext(context) {
|
|
2685
|
-
this._context = context;
|
|
2686
|
-
this._auth = new AuthContext(context.auth);
|
|
2687
|
-
this._db = createDb(
|
|
2688
|
-
context.databases,
|
|
2689
|
-
this._executeDbBatch.bind(this)
|
|
2690
|
-
);
|
|
2691
|
-
}
|
|
2692
|
-
/**
|
|
2693
|
-
* @internal Try to hydrate context synchronously from sandbox globals.
|
|
2694
|
-
* Called in the constructor when CALLBACK_TOKEN auth is detected.
|
|
2695
|
-
*
|
|
2696
|
-
* The MindStudio sandbox pre-populates `globalThis.ai` with:
|
|
2697
|
-
* - `ai.auth`: { userId, roleAssignments[] }
|
|
2698
|
-
* - `ai.databases`: [{ id, name, tables[] }]
|
|
2699
|
-
*/
|
|
2700
|
-
_trySandboxHydration() {
|
|
2701
|
-
const ai = globalThis.ai;
|
|
2702
|
-
if (ai?.auth && ai?.databases) {
|
|
2703
|
-
this._applyContext({
|
|
2704
|
-
auth: ai.auth,
|
|
2705
|
-
databases: ai.databases
|
|
2706
|
-
});
|
|
2707
|
-
}
|
|
2708
|
-
}
|
|
2709
|
-
/**
|
|
2710
|
-
* @internal Execute a batch of SQL queries against a managed database.
|
|
2711
|
-
* Used as the `executeBatch` callback for Table/Query instances.
|
|
2712
|
-
*
|
|
2713
|
-
* Calls `POST /_internal/v2/db/query` directly with the hook token
|
|
2714
|
-
* (raw, no Bearer prefix). All queries run on a single SQLite connection,
|
|
2715
|
-
* enabling RETURNING clauses and multi-statement batches.
|
|
2716
|
-
*/
|
|
2717
|
-
async _executeDbBatch(databaseId, queries) {
|
|
2718
|
-
const url = `${this._httpConfig.baseUrl}/_internal/v2/db/query`;
|
|
2719
|
-
const res = await fetch(url, {
|
|
2720
|
-
method: "POST",
|
|
2721
|
-
headers: {
|
|
2722
|
-
"Content-Type": "application/json",
|
|
2723
|
-
Authorization: this._token
|
|
2724
|
-
},
|
|
2725
|
-
body: JSON.stringify({ databaseId, queries })
|
|
2726
|
-
});
|
|
2727
|
-
if (!res.ok) {
|
|
2728
|
-
let message = `Database query failed: ${res.status} ${res.statusText}`;
|
|
2729
|
-
let code = "db_query_error";
|
|
2730
|
-
try {
|
|
2731
|
-
const text = await res.text();
|
|
2732
|
-
try {
|
|
2733
|
-
const body = JSON.parse(text);
|
|
2734
|
-
const errMsg = body.error ?? body.message ?? body.details;
|
|
2735
|
-
if (errMsg) message = errMsg;
|
|
2736
|
-
if (body.code) code = body.code;
|
|
2737
|
-
} catch {
|
|
2738
|
-
if (text && text.length < 500) message = text;
|
|
2739
|
-
}
|
|
2740
|
-
} catch {
|
|
2741
|
-
}
|
|
2742
|
-
throw new MindStudioError(
|
|
2743
|
-
`[db] ${message}`,
|
|
2744
|
-
code,
|
|
2745
|
-
res.status
|
|
2746
|
-
);
|
|
2747
|
-
}
|
|
2748
|
-
const data = await res.json();
|
|
2749
|
-
return data.results;
|
|
2750
|
-
}
|
|
2751
|
-
/**
|
|
2752
|
-
* @internal Create a lazy Db proxy that auto-hydrates context.
|
|
2753
|
-
*
|
|
2754
|
-
* defineTable() returns Table instances immediately (no async needed).
|
|
2755
|
-
* But the Table's executeBatch callback is wrapped to call ensureContext()
|
|
2756
|
-
* before the first query, so context is fetched lazily.
|
|
2757
|
-
*/
|
|
2758
|
-
_createLazyDb() {
|
|
2759
|
-
const agent = this;
|
|
2760
|
-
return {
|
|
2761
|
-
defineTable(name, options) {
|
|
2762
|
-
const databaseHint = options?.database;
|
|
2763
|
-
return new Table({
|
|
2764
|
-
databaseId: "",
|
|
2765
|
-
tableName: name,
|
|
2766
|
-
columns: [],
|
|
2767
|
-
unique: options?.unique,
|
|
2768
|
-
defaults: options?.defaults,
|
|
2769
|
-
executeBatch: async (queries) => {
|
|
2770
|
-
await agent.ensureContext();
|
|
2771
|
-
const databases = agent._context.databases;
|
|
2772
|
-
let targetDb;
|
|
2773
|
-
if (databaseHint) {
|
|
2774
|
-
targetDb = databases.find(
|
|
2775
|
-
(d) => d.id === databaseHint || d.name === databaseHint
|
|
2776
|
-
);
|
|
2777
|
-
} else {
|
|
2778
|
-
targetDb = databases.find(
|
|
2779
|
-
(d) => d.tables.some((t) => t.name === name)
|
|
2780
|
-
);
|
|
2781
|
-
}
|
|
2782
|
-
const databaseId = targetDb?.id ?? databases[0]?.id ?? "";
|
|
2783
|
-
return agent._executeDbBatch(databaseId, queries);
|
|
2784
|
-
}
|
|
2785
|
-
});
|
|
2786
|
-
},
|
|
2787
|
-
// Time helpers work without context
|
|
2788
|
-
now: () => Date.now(),
|
|
2789
|
-
days: (n) => n * 864e5,
|
|
2790
|
-
hours: (n) => n * 36e5,
|
|
2791
|
-
minutes: (n) => n * 6e4,
|
|
2792
|
-
ago: (ms) => Date.now() - ms,
|
|
2793
|
-
fromNow: (ms) => Date.now() + ms,
|
|
2794
|
-
// Batch needs context — hydrate first, then delegate to real db
|
|
2795
|
-
batch: ((...queries) => {
|
|
2796
|
-
return (async () => {
|
|
2797
|
-
await agent.ensureContext();
|
|
2798
|
-
return agent._db.batch(...queries);
|
|
2799
|
-
})();
|
|
2800
|
-
})
|
|
2801
|
-
};
|
|
2802
|
-
}
|
|
2803
|
-
// -------------------------------------------------------------------------
|
|
2804
|
-
// Helper methods — user resolution
|
|
2805
|
-
// -------------------------------------------------------------------------
|
|
2806
|
-
/**
|
|
2807
|
-
* Resolve a single user ID to display info (name, email, profile picture).
|
|
2808
|
-
*
|
|
2809
|
-
* Use this when you have a `User`-typed field value and need the person's
|
|
2810
|
-
* display name, email, or avatar. Returns null if the user ID is not found.
|
|
2811
|
-
*
|
|
2812
|
-
* Also available as a top-level import:
|
|
2813
|
-
* ```ts
|
|
2814
|
-
* import { resolveUser } from '@mindstudio-ai/agent';
|
|
2815
|
-
* ```
|
|
2816
|
-
*
|
|
2817
|
-
* @param userId - The user ID to resolve (a `User` branded string or plain UUID)
|
|
2818
|
-
* @returns Resolved user info, or null if not found
|
|
2819
|
-
*
|
|
2820
|
-
* @example
|
|
2821
|
-
* ```ts
|
|
2822
|
-
* const user = await agent.resolveUser(order.requestedBy);
|
|
2823
|
-
* if (user) {
|
|
2824
|
-
* console.log(user.name); // "Jane Smith"
|
|
2825
|
-
* console.log(user.email); // "jane@example.com"
|
|
2826
|
-
* console.log(user.profilePictureUrl); // "https://..." or null
|
|
2827
|
-
* }
|
|
2828
|
-
* ```
|
|
2829
|
-
*/
|
|
2830
|
-
async resolveUser(userId) {
|
|
2831
|
-
const { users } = await this.resolveUsers([userId]);
|
|
2832
|
-
return users[0] ?? null;
|
|
2833
|
-
}
|
|
2834
|
-
/**
|
|
2835
|
-
* Resolve multiple user IDs to display info in a single request.
|
|
2836
|
-
* Maximum 100 user IDs per request.
|
|
2837
|
-
*
|
|
2838
|
-
* Use this for batch resolution when you have multiple user references
|
|
2839
|
-
* to display (e.g. all approvers on a purchase order, all team members).
|
|
2840
|
-
*
|
|
2841
|
-
* @param userIds - Array of user IDs to resolve (max 100)
|
|
2842
|
-
* @returns Object with `users` array of resolved user info
|
|
2843
|
-
*
|
|
2844
|
-
* @example
|
|
2845
|
-
* ```ts
|
|
2846
|
-
* // Resolve all approvers at once
|
|
2847
|
-
* const approverIds = approvals.map(a => a.assignedTo);
|
|
2848
|
-
* const { users } = await agent.resolveUsers(approverIds);
|
|
2849
|
-
*
|
|
2850
|
-
* for (const u of users) {
|
|
2851
|
-
* console.log(`${u.name} (${u.email})`);
|
|
2852
|
-
* }
|
|
2853
|
-
* ```
|
|
2854
|
-
*/
|
|
2855
|
-
async resolveUsers(userIds) {
|
|
2856
|
-
const { data } = await request(
|
|
2857
|
-
this._httpConfig,
|
|
2858
|
-
"POST",
|
|
2859
|
-
"/helpers/resolve-users",
|
|
2860
|
-
{ userIds }
|
|
2861
|
-
);
|
|
2862
|
-
return data;
|
|
2863
|
-
}
|
|
2864
|
-
// -------------------------------------------------------------------------
|
|
2865
|
-
// App context
|
|
2866
|
-
// -------------------------------------------------------------------------
|
|
2867
|
-
/**
|
|
2868
|
-
* Get auth and database context for an app.
|
|
2869
|
-
*
|
|
2870
|
-
* Returns role assignments and managed database schemas. Useful for
|
|
2871
|
-
* hydrating `auth` and `db` namespaces when running outside the sandbox.
|
|
2872
|
-
*
|
|
2873
|
-
* When called with a CALLBACK_TOKEN (managed mode), `appId` is optional —
|
|
2874
|
-
* the platform resolves the app from the token. With an API key, `appId`
|
|
2875
|
-
* is required.
|
|
2876
|
-
*
|
|
2877
|
-
* ```ts
|
|
2878
|
-
* const ctx = await agent.getAppContext('your-app-id');
|
|
2879
|
-
* console.log(ctx.auth.roleAssignments, ctx.databases);
|
|
2880
|
-
* ```
|
|
2881
|
-
*/
|
|
2882
|
-
async getAppContext(appId) {
|
|
2883
|
-
const query = appId ? `?appId=${encodeURIComponent(appId)}` : "";
|
|
2884
|
-
const { data } = await request(
|
|
2885
|
-
this._httpConfig,
|
|
2886
|
-
"GET",
|
|
2887
|
-
`/helpers/app-context${query}`
|
|
2888
|
-
);
|
|
2889
|
-
return data;
|
|
2890
|
-
}
|
|
2891
|
-
// -------------------------------------------------------------------------
|
|
2892
|
-
// Account methods
|
|
2893
|
-
// -------------------------------------------------------------------------
|
|
2894
|
-
/** Update the display name of the authenticated user/agent. */
|
|
2895
|
-
async changeName(displayName) {
|
|
2896
|
-
await request(this._httpConfig, "POST", "/account/change-name", {
|
|
2897
|
-
name: displayName
|
|
2898
|
-
});
|
|
2899
|
-
}
|
|
2900
|
-
/** Update the profile picture of the authenticated user/agent. */
|
|
2901
|
-
async changeProfilePicture(url) {
|
|
2902
|
-
await request(this._httpConfig, "POST", "/account/change-profile-picture", {
|
|
2903
|
-
url
|
|
2904
|
-
});
|
|
2905
|
-
}
|
|
2906
|
-
/**
|
|
2907
|
-
* Upload a file to the MindStudio CDN.
|
|
2908
|
-
*
|
|
2909
|
-
* Gets a signed upload URL, PUTs the file content, and returns the
|
|
2910
|
-
* permanent public URL.
|
|
2911
|
-
*/
|
|
2912
|
-
async uploadFile(content, options) {
|
|
2913
|
-
const { data } = await request(
|
|
2914
|
-
this._httpConfig,
|
|
2915
|
-
"POST",
|
|
2916
|
-
"/account/upload",
|
|
2917
|
-
{
|
|
2918
|
-
extension: options.extension,
|
|
2919
|
-
...options.type != null && { type: options.type }
|
|
2920
|
-
}
|
|
2921
|
-
);
|
|
2922
|
-
const buf = content.buffer.slice(
|
|
2923
|
-
content.byteOffset,
|
|
2924
|
-
content.byteOffset + content.byteLength
|
|
2925
|
-
);
|
|
2926
|
-
const res = await fetch(data.uploadUrl, {
|
|
2927
|
-
method: "PUT",
|
|
2928
|
-
body: buf,
|
|
2929
|
-
headers: options.type ? { "Content-Type": options.type } : {}
|
|
2930
|
-
});
|
|
2931
|
-
if (!res.ok) {
|
|
2932
|
-
const errorBody = await res.json().catch(() => ({}));
|
|
2933
|
-
throw new MindStudioError(
|
|
2934
|
-
errorBody.message ?? errorBody.error ?? `Upload failed: ${res.status} ${res.statusText}`,
|
|
2935
|
-
errorBody.code ?? "upload_error",
|
|
2936
|
-
res.status,
|
|
2937
|
-
errorBody
|
|
2938
|
-
);
|
|
2939
|
-
}
|
|
2940
|
-
return { url: data.url };
|
|
2941
|
-
}
|
|
2942
|
-
};
|
|
2943
|
-
function sleep2(ms) {
|
|
2944
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2945
|
-
}
|
|
2946
|
-
applyStepMethods(MindStudioAgent);
|
|
2947
|
-
function resolveToken(provided, config) {
|
|
2948
|
-
if (process.env.CALLBACK_TOKEN)
|
|
2949
|
-
return { token: process.env.CALLBACK_TOKEN, authType: "internal" };
|
|
2950
|
-
if (provided) return { token: provided, authType: "apiKey" };
|
|
2951
|
-
if (process.env.MINDSTUDIO_API_KEY)
|
|
2952
|
-
return { token: process.env.MINDSTUDIO_API_KEY, authType: "apiKey" };
|
|
2953
|
-
if (config?.apiKey)
|
|
2954
|
-
return { token: config.apiKey, authType: "apiKey" };
|
|
2955
|
-
throw new MindStudioError(
|
|
2956
|
-
"No API key provided. Run `mindstudio login`, pass `apiKey` to the constructor, or set the MINDSTUDIO_API_KEY environment variable.",
|
|
2957
|
-
"missing_api_key",
|
|
2958
|
-
401
|
|
2959
|
-
);
|
|
2960
|
-
}
|
|
2961
|
-
|
|
2962
|
-
// src/generated/snippets.ts
|
|
2963
|
-
var monacoSnippets = {
|
|
2964
|
-
"activeCampaignAddNote": { fields: [["contactId", "string"], ["note", "string"]], outputKeys: [] },
|
|
2965
|
-
"activeCampaignCreateContact": { fields: [["email", "string"], ["firstName", "string"], ["lastName", "string"], ["phone", "string"], ["accountId", "string"], ["customFields", "object"]], outputKeys: ["contactId"] },
|
|
2966
|
-
"addSubtitlesToVideo": { fields: [["videoUrl", "string"], ["language", "string"], ["fontName", "string"], ["fontSize", "number"], ["fontWeight", ["normal", "bold", "black"]], ["fontColor", ["white", "black", "red", "green", "blue", "yellow", "orange", "purple", "pink", "brown", "gray", "cyan", "magenta"]], ["highlightColor", ["white", "black", "red", "green", "blue", "yellow", "orange", "purple", "pink", "brown", "gray", "cyan", "magenta"]], ["strokeWidth", "number"], ["strokeColor", ["black", "white", "red", "green", "blue", "yellow", "orange", "purple", "pink", "brown", "gray", "cyan", "magenta"]], ["backgroundColor", ["black", "white", "red", "green", "blue", "yellow", "orange", "purple", "pink", "brown", "gray", "cyan", "magenta", "none"]], ["backgroundOpacity", "number"], ["position", ["top", "center", "bottom"]], ["yOffset", "number"], ["wordsPerSubtitle", "number"], ["enableAnimation", "boolean"]], outputKeys: ["videoUrl"] },
|
|
2967
|
-
"airtableCreateUpdateRecord": { fields: [["baseId", "string"], ["tableId", "string"], ["fields", "string"], ["recordData", "object"]], outputKeys: ["recordId"] },
|
|
2968
|
-
"airtableDeleteRecord": { fields: [["baseId", "string"], ["tableId", "string"], ["recordId", "string"]], outputKeys: ["deleted"] },
|
|
2969
|
-
"airtableGetRecord": { fields: [["baseId", "string"], ["tableId", "string"], ["recordId", "string"]], outputKeys: ["record"] },
|
|
2970
|
-
"airtableGetTableRecords": { fields: [["baseId", "string"], ["tableId", "string"]], outputKeys: ["records"] },
|
|
2971
|
-
"analyzeImage": { fields: [["prompt", "string"], ["imageUrl", "string"]], outputKeys: ["analysis"] },
|
|
2972
|
-
"analyzeVideo": { fields: [["prompt", "string"], ["videoUrl", "string"]], outputKeys: ["analysis"] },
|
|
2973
|
-
"captureThumbnail": { fields: [["videoUrl", "string"], ["at", "string"]], outputKeys: ["thumbnailUrl"] },
|
|
2974
|
-
"checkAppRole": { fields: [["roleName", "string"]], outputKeys: ["hasRole", "userRoles"] },
|
|
2975
|
-
"codaCreateUpdatePage": { fields: [["pageData", "object"]], outputKeys: ["pageId"] },
|
|
2976
|
-
"codaCreateUpdateRow": { fields: [["docId", "string"], ["tableId", "string"], ["rowData", "object"]], outputKeys: ["rowId"] },
|
|
2977
|
-
"codaFindRow": { fields: [["docId", "string"], ["tableId", "string"], ["rowData", "object"]], outputKeys: ["row"] },
|
|
2978
|
-
"codaGetPage": { fields: [["docId", "string"], ["pageId", "string"]], outputKeys: ["content"] },
|
|
2979
|
-
"codaGetTableRows": { fields: [["docId", "string"], ["tableId", "string"]], outputKeys: ["rows"] },
|
|
2980
|
-
"convertPdfToImages": { fields: [["pdfUrl", "string"]], outputKeys: ["imageUrls"] },
|
|
2981
|
-
"createDataSource": { fields: [["name", "string"]], outputKeys: [] },
|
|
2982
|
-
"createGmailDraft": { fields: [["to", "string"], ["subject", "string"], ["message", "string"], ["messageType", ["plain", "html", "markdown"]]], outputKeys: ["draftId"] },
|
|
2983
|
-
"createGoogleCalendarEvent": { fields: [["summary", "string"], ["startDateTime", "string"], ["endDateTime", "string"]], outputKeys: ["eventId", "htmlLink"] },
|
|
2984
|
-
"createGoogleDoc": { fields: [["title", "string"], ["text", "string"], ["textType", ["plain", "html", "markdown"]]], outputKeys: ["documentUrl"] },
|
|
2985
|
-
"createGoogleSheet": { fields: [["title", "string"], ["text", "string"]], outputKeys: ["spreadsheetUrl"] },
|
|
2986
|
-
"deleteDataSource": { fields: [["dataSourceId", "string"]], outputKeys: [] },
|
|
2987
|
-
"deleteDataSourceDocument": { fields: [["dataSourceId", "string"], ["documentId", "string"]], outputKeys: [] },
|
|
2988
|
-
"deleteGmailEmail": { fields: [["messageId", "string"]], outputKeys: [] },
|
|
2989
|
-
"deleteGoogleCalendarEvent": { fields: [["eventId", "string"]], outputKeys: [] },
|
|
2990
|
-
"deleteGoogleSheetRows": { fields: [["documentId", "string"], ["startRow", "string"], ["endRow", "string"]], outputKeys: [] },
|
|
2991
|
-
"detectChanges": { fields: [["mode", ["ai", "comparison"]], ["input", "string"]], outputKeys: ["hasChanged", "currentValue", "previousValue", "isFirstRun"] },
|
|
2992
|
-
"detectPII": { fields: [["input", "string"], ["language", "string"], ["entities", "array"]], outputKeys: ["detected", "detections"] },
|
|
2993
|
-
"discordEditMessage": { fields: [["botToken", "string"], ["channelId", "string"], ["messageId", "string"], ["text", "string"]], outputKeys: [] },
|
|
2994
|
-
"discordSendFollowUp": { fields: [["applicationId", "string"], ["interactionToken", "string"], ["text", "string"]], outputKeys: ["messageId"] },
|
|
2995
|
-
"discordSendMessage": { fields: [["mode", ["edit", "send"]], ["text", "string"]], outputKeys: [] },
|
|
2996
|
-
"downloadVideo": { fields: [["videoUrl", "string"], ["format", ["mp4", "mp3"]]], outputKeys: ["videoUrl"] },
|
|
2997
|
-
"enhanceImageGenerationPrompt": { fields: [["initialPrompt", "string"], ["includeNegativePrompt", "boolean"], ["systemPrompt", "string"]], outputKeys: ["prompt"] },
|
|
2998
|
-
"enhanceVideoGenerationPrompt": { fields: [["initialPrompt", "string"], ["includeNegativePrompt", "boolean"], ["systemPrompt", "string"]], outputKeys: ["prompt"] },
|
|
2999
|
-
"enrichPerson": { fields: [["params", "object"]], outputKeys: ["data"] },
|
|
3000
|
-
"extractAudioFromVideo": { fields: [["videoUrl", "string"]], outputKeys: ["audioUrl"] },
|
|
3001
|
-
"extractText": { fields: [["url", "string"]], outputKeys: ["text"] },
|
|
3002
|
-
"fetchDataSourceDocument": { fields: [["dataSourceId", "string"], ["documentId", "string"]], outputKeys: [] },
|
|
3003
|
-
"fetchGoogleDoc": { fields: [["documentId", "string"], ["exportType", ["html", "markdown", "json", "plain"]]], outputKeys: ["content"] },
|
|
3004
|
-
"fetchGoogleSheet": { fields: [["spreadsheetId", "string"], ["range", "string"], ["exportType", ["csv", "json"]]], outputKeys: ["content"] },
|
|
3005
|
-
"fetchSlackChannelHistory": { fields: [["channelId", "string"]], outputKeys: ["messages"] },
|
|
3006
|
-
"fetchYoutubeCaptions": { fields: [["videoUrl", "string"], ["exportType", ["text", "json"]], ["language", "string"]], outputKeys: ["transcripts"] },
|
|
3007
|
-
"fetchYoutubeChannel": { fields: [["channelUrl", "string"]], outputKeys: [] },
|
|
3008
|
-
"fetchYoutubeComments": { fields: [["videoUrl", "string"], ["exportType", ["text", "json"]], ["limitPages", "string"]], outputKeys: ["comments"] },
|
|
3009
|
-
"fetchYoutubeVideo": { fields: [["videoUrl", "string"]], outputKeys: [] },
|
|
3010
|
-
"generateAsset": { fields: [["source", "string"], ["sourceType", ["html", "markdown", "spa", "raw", "dynamic", "customInterface"]], ["outputFormat", ["pdf", "png", "html", "mp4", "openGraph"]], ["pageSize", ["full", "letter", "A4", "custom"]], ["testData", "object"]], outputKeys: ["url"] },
|
|
3011
|
-
"generateChart": { fields: [["chart", "object"]], outputKeys: ["chartUrl"] },
|
|
3012
|
-
"generateImage": { fields: [["prompt", "string"]], outputKeys: ["imageUrl"] },
|
|
3013
|
-
"generateLipsync": { fields: [], outputKeys: [] },
|
|
3014
|
-
"generateMusic": { fields: [["text", "string"]], outputKeys: [] },
|
|
3015
|
-
"generatePdf": { fields: [["source", "string"], ["sourceType", ["html", "markdown", "spa", "raw", "dynamic", "customInterface"]], ["outputFormat", ["pdf", "png", "html", "mp4", "openGraph"]], ["pageSize", ["full", "letter", "A4", "custom"]], ["testData", "object"]], outputKeys: ["url"] },
|
|
3016
|
-
"generateStaticVideoFromImage": { fields: [["imageUrl", "string"], ["duration", "string"]], outputKeys: ["videoUrl"] },
|
|
3017
|
-
"generateText": { fields: [["message", "string"]], outputKeys: ["content"] },
|
|
3018
|
-
"generateVideo": { fields: [["prompt", "string"]], outputKeys: ["videoUrl"] },
|
|
3019
|
-
"getGmailAttachments": { fields: [["messageId", "string"]], outputKeys: [] },
|
|
3020
|
-
"getGmailDraft": { fields: [["draftId", "string"]], outputKeys: ["draftId", "messageId", "subject", "to", "from", "body"] },
|
|
3021
|
-
"getGmailEmail": { fields: [["messageId", "string"]], outputKeys: ["messageId", "subject", "from", "to", "date", "body", "labels"] },
|
|
3022
|
-
"getGmailUnreadCount": { fields: [], outputKeys: [] },
|
|
3023
|
-
"getGoogleCalendarEvent": { fields: [["eventId", "string"], ["exportType", ["json", "text"]]], outputKeys: ["event"] },
|
|
3024
|
-
"getGoogleDriveFile": { fields: [["fileId", "string"]], outputKeys: ["url", "name", "mimeType", "size"] },
|
|
3025
|
-
"getGoogleSheetInfo": { fields: [["documentId", "string"]], outputKeys: ["title", "sheets"] },
|
|
3026
|
-
"getMediaMetadata": { fields: [["mediaUrl", "string"]], outputKeys: ["metadata"] },
|
|
3027
|
-
"httpRequest": { fields: [["url", "string"], ["method", "string"], ["headers", "object"], ["queryParams", "object"], ["body", "string"], ["bodyItems", "object"], ["contentType", ["none", "application/json", "application/x-www-form-urlencoded", "multipart/form-data", "custom"]], ["customContentType", "string"]], outputKeys: ["ok", "status", "statusText", "response"] },
|
|
3028
|
-
"hubspotCreateCompany": { fields: [["company", "object"], ["enabledProperties", "array"]], outputKeys: ["companyId"] },
|
|
3029
|
-
"hubspotCreateContact": { fields: [["contact", "object"], ["enabledProperties", "array"], ["companyDomain", "string"]], outputKeys: ["contactId"] },
|
|
3030
|
-
"hubspotGetCompany": { fields: [["searchBy", ["domain", "id"]], ["companyDomain", "string"], ["companyId", "string"], ["additionalProperties", "array"]], outputKeys: ["company"] },
|
|
3031
|
-
"hubspotGetContact": { fields: [["searchBy", ["email", "id"]], ["contactEmail", "string"], ["contactId", "string"], ["additionalProperties", "array"]], outputKeys: ["contact"] },
|
|
3032
|
-
"hunterApiCompanyEnrichment": { fields: [["domain", "string"]], outputKeys: ["data"] },
|
|
3033
|
-
"hunterApiDomainSearch": { fields: [["domain", "string"]], outputKeys: ["data"] },
|
|
3034
|
-
"hunterApiEmailFinder": { fields: [["domain", "string"], ["firstName", "string"], ["lastName", "string"]], outputKeys: ["data"] },
|
|
3035
|
-
"hunterApiEmailVerification": { fields: [["email", "string"]], outputKeys: ["data"] },
|
|
3036
|
-
"hunterApiPersonEnrichment": { fields: [["email", "string"]], outputKeys: ["data"] },
|
|
3037
|
-
"imageFaceSwap": { fields: [["imageUrl", "string"], ["faceImageUrl", "string"], ["engine", "string"]], outputKeys: ["imageUrl"] },
|
|
3038
|
-
"imageRemoveWatermark": { fields: [["imageUrl", "string"], ["engine", "string"]], outputKeys: ["imageUrl"] },
|
|
3039
|
-
"insertVideoClips": { fields: [["baseVideoUrl", "string"], ["overlayVideos", "array"]], outputKeys: ["videoUrl"] },
|
|
3040
|
-
"listDataSources": { fields: [], outputKeys: [] },
|
|
3041
|
-
"listGmailDrafts": { fields: [["exportType", ["json", "text"]]], outputKeys: ["drafts"] },
|
|
3042
|
-
"listGmailLabels": { fields: [], outputKeys: [] },
|
|
3043
|
-
"listGoogleCalendarEvents": { fields: [["limit", "number"], ["exportType", ["json", "text"]]], outputKeys: ["events"] },
|
|
3044
|
-
"listGoogleDriveFiles": { fields: [["exportType", ["json", "text"]]], outputKeys: ["files"] },
|
|
3045
|
-
"listRecentGmailEmails": { fields: [["exportType", ["json", "text"]], ["limit", "string"]], outputKeys: [] },
|
|
3046
|
-
"logic": { fields: [["context", "string"], ["cases", "array"]], outputKeys: ["selectedCase"] },
|
|
3047
|
-
"makeDotComRunScenario": { fields: [["webhookUrl", "string"], ["input", "object"]], outputKeys: ["data"] },
|
|
3048
|
-
"mergeAudio": { fields: [["mp3Urls", "array"]], outputKeys: ["audioUrl"] },
|
|
3049
|
-
"mergeVideos": { fields: [["videoUrls", "array"]], outputKeys: ["videoUrl"] },
|
|
3050
|
-
"mixAudioIntoVideo": { fields: [["videoUrl", "string"], ["audioUrl", "string"], ["options", "object"]], outputKeys: ["videoUrl"] },
|
|
3051
|
-
"muteVideo": { fields: [["videoUrl", "string"]], outputKeys: ["videoUrl"] },
|
|
3052
|
-
"n8nRunNode": { fields: [["method", "string"], ["authentication", ["none", "basic", "string"]], ["user", "string"], ["password", "string"], ["webhookUrl", "string"], ["input", "object"]], outputKeys: ["data"] },
|
|
3053
|
-
"notionCreatePage": { fields: [["pageId", "string"], ["content", "string"], ["title", "string"]], outputKeys: ["pageId", "pageUrl"] },
|
|
3054
|
-
"notionUpdatePage": { fields: [["pageId", "string"], ["content", "string"], ["mode", ["append", "overwrite"]]], outputKeys: ["pageId", "pageUrl"] },
|
|
3055
|
-
"peopleSearch": { fields: [["smartQuery", "string"], ["enrichPeople", "boolean"], ["enrichOrganizations", "boolean"], ["limit", "string"], ["page", "string"], ["params", "object"]], outputKeys: ["results"] },
|
|
3056
|
-
"postToLinkedIn": { fields: [["message", "string"], ["visibility", ["PUBLIC", "CONNECTIONS"]]], outputKeys: [] },
|
|
3057
|
-
"postToSlackChannel": { fields: [["channelId", "string"], ["messageType", ["string", "blocks"]], ["message", "string"]], outputKeys: [] },
|
|
3058
|
-
"postToX": { fields: [["text", "string"]], outputKeys: [] },
|
|
3059
|
-
"postToZapier": { fields: [["webhookUrl", "string"], ["input", "object"]], outputKeys: ["data"] },
|
|
3060
|
-
"queryAppDatabase": { fields: [["databaseId", "string"], ["sql", "string"]], outputKeys: ["rows", "changes"] },
|
|
3061
|
-
"queryDataSource": { fields: [["dataSourceId", "string"], ["query", "string"], ["maxResults", "number"]], outputKeys: ["text", "chunks", "query", "citations", "latencyMs"] },
|
|
3062
|
-
"queryExternalDatabase": { fields: [["query", "string"], ["outputFormat", ["json", "csv"]]], outputKeys: ["data"] },
|
|
3063
|
-
"redactPII": { fields: [["input", "string"], ["language", "string"], ["entities", "array"]], outputKeys: ["text"] },
|
|
3064
|
-
"removeBackgroundFromImage": { fields: [["imageUrl", "string"]], outputKeys: ["imageUrl"] },
|
|
3065
|
-
"replyToGmailEmail": { fields: [["messageId", "string"], ["message", "string"], ["messageType", ["plain", "html", "markdown"]]], outputKeys: ["messageId"] },
|
|
3066
|
-
"resizeVideo": { fields: [["videoUrl", "string"], ["mode", ["fit", "exact"]]], outputKeys: ["videoUrl"] },
|
|
3067
|
-
"runFromConnectorRegistry": { fields: [["actionId", "string"], ["displayName", "string"], ["icon", "string"], ["configurationValues", "object"]], outputKeys: ["data"] },
|
|
3068
|
-
"runPackagedWorkflow": { fields: [["appId", "string"], ["workflowId", "string"], ["inputVariables", "object"], ["outputVariables", "object"], ["name", "string"]], outputKeys: ["data"] },
|
|
3069
|
-
"scrapeFacebookPage": { fields: [["pageUrl", "string"]], outputKeys: ["data"] },
|
|
3070
|
-
"scrapeFacebookPosts": { fields: [["pageUrl", "string"]], outputKeys: ["data"] },
|
|
3071
|
-
"scrapeInstagramComments": { fields: [["postUrl", "string"], ["resultsLimit", "string"]], outputKeys: ["data"] },
|
|
3072
|
-
"scrapeInstagramMentions": { fields: [["profileUrl", "string"], ["resultsLimit", "string"]], outputKeys: ["data"] },
|
|
3073
|
-
"scrapeInstagramPosts": { fields: [["profileUrl", "string"], ["resultsLimit", "string"], ["onlyPostsNewerThan", "string"]], outputKeys: ["data"] },
|
|
3074
|
-
"scrapeInstagramProfile": { fields: [["profileUrl", "string"]], outputKeys: ["data"] },
|
|
3075
|
-
"scrapeInstagramReels": { fields: [["profileUrl", "string"], ["resultsLimit", "string"]], outputKeys: ["data"] },
|
|
3076
|
-
"scrapeLinkedInCompany": { fields: [["url", "string"]], outputKeys: ["company"] },
|
|
3077
|
-
"scrapeLinkedInProfile": { fields: [["url", "string"]], outputKeys: ["profile"] },
|
|
3078
|
-
"scrapeMetaThreadsProfile": { fields: [["profileUrl", "string"]], outputKeys: ["data"] },
|
|
3079
|
-
"scrapeUrl": { fields: [["url", "string"]], outputKeys: ["content"] },
|
|
3080
|
-
"scrapeXPost": { fields: [["url", "string"]], outputKeys: ["post"] },
|
|
3081
|
-
"scrapeXProfile": { fields: [["url", "string"]], outputKeys: ["profile"] },
|
|
3082
|
-
"screenshotUrl": { fields: [["url", "string"]], outputKeys: ["screenshotUrl"] },
|
|
3083
|
-
"searchGmailEmails": { fields: [["query", "string"], ["exportType", ["json", "text"]], ["limit", "string"]], outputKeys: ["emails"] },
|
|
3084
|
-
"searchGoogle": { fields: [["query", "string"], ["exportType", ["text", "json"]]], outputKeys: ["results"] },
|
|
3085
|
-
"searchGoogleCalendarEvents": { fields: [["exportType", ["json", "text"]]], outputKeys: ["events"] },
|
|
3086
|
-
"searchGoogleDrive": { fields: [["query", "string"], ["exportType", ["json", "text"]]], outputKeys: ["files"] },
|
|
3087
|
-
"searchGoogleImages": { fields: [["query", "string"], ["exportType", ["text", "json"]]], outputKeys: ["images"] },
|
|
3088
|
-
"searchGoogleNews": { fields: [["text", "string"], ["exportType", ["text", "json"]]], outputKeys: ["articles"] },
|
|
3089
|
-
"searchGoogleTrends": { fields: [["text", "string"], ["hl", "string"], ["geo", "string"], ["data_type", ["TIMESERIES", "GEO_MAP", "GEO_MAP_0", "RELATED_TOPICS", "RELATED_QUERIES"]], ["cat", "string"], ["date", "string"], ["ts", "string"]], outputKeys: ["trends"] },
|
|
3090
|
-
"searchPerplexity": { fields: [["query", "string"], ["exportType", ["text", "json"]]], outputKeys: ["results"] },
|
|
3091
|
-
"searchXPosts": { fields: [["query", "string"], ["scope", ["recent", "all"]], ["options", "object"]], outputKeys: ["posts"] },
|
|
3092
|
-
"searchYoutube": { fields: [["query", "string"], ["limitPages", "string"], ["filter", "string"], ["filterType", "string"]], outputKeys: ["results"] },
|
|
3093
|
-
"searchYoutubeTrends": { fields: [["bp", ["now", "music", "gaming", "films"]], ["hl", "string"], ["gl", "string"]], outputKeys: [] },
|
|
3094
|
-
"sendEmail": { fields: [["subject", "string"], ["body", "string"]], outputKeys: ["recipients"] },
|
|
3095
|
-
"sendGmailDraft": { fields: [["draftId", "string"]], outputKeys: [] },
|
|
3096
|
-
"sendGmailMessage": { fields: [["to", "string"], ["subject", "string"], ["message", "string"], ["messageType", ["plain", "html", "markdown"]]], outputKeys: ["messageId"] },
|
|
3097
|
-
"sendSMS": { fields: [["body", "string"]], outputKeys: [] },
|
|
3098
|
-
"setGmailReadStatus": { fields: [["messageIds", "string"], ["markAsRead", "boolean"]], outputKeys: [] },
|
|
3099
|
-
"setRunTitle": { fields: [["title", "string"]], outputKeys: [] },
|
|
3100
|
-
"setVariable": { fields: [["value", "string"]], outputKeys: [] },
|
|
3101
|
-
"telegramEditMessage": { fields: [["botToken", "string"], ["chatId", "string"], ["messageId", "string"], ["text", "string"]], outputKeys: [] },
|
|
3102
|
-
"telegramReplyToMessage": { fields: [["botToken", "string"], ["chatId", "string"], ["replyToMessageId", "string"], ["text", "string"]], outputKeys: ["messageId"] },
|
|
3103
|
-
"telegramSendAudio": { fields: [["botToken", "string"], ["chatId", "string"], ["audioUrl", "string"], ["mode", ["audio", "voice"]]], outputKeys: [] },
|
|
3104
|
-
"telegramSendFile": { fields: [["botToken", "string"], ["chatId", "string"], ["fileUrl", "string"]], outputKeys: [] },
|
|
3105
|
-
"telegramSendImage": { fields: [["botToken", "string"], ["chatId", "string"], ["imageUrl", "string"]], outputKeys: [] },
|
|
3106
|
-
"telegramSendMessage": { fields: [["botToken", "string"], ["chatId", "string"], ["text", "string"]], outputKeys: ["messageId"] },
|
|
3107
|
-
"telegramSendVideo": { fields: [["botToken", "string"], ["chatId", "string"], ["videoUrl", "string"]], outputKeys: [] },
|
|
3108
|
-
"telegramSetTyping": { fields: [["botToken", "string"], ["chatId", "string"]], outputKeys: [] },
|
|
3109
|
-
"textToSpeech": { fields: [["text", "string"]], outputKeys: ["audioUrl"] },
|
|
3110
|
-
"transcribeAudio": { fields: [["audioUrl", "string"], ["prompt", "string"]], outputKeys: ["text"] },
|
|
3111
|
-
"trimMedia": { fields: [["inputUrl", "string"]], outputKeys: ["mediaUrl"] },
|
|
3112
|
-
"updateGmailLabels": { fields: [["query", "string"], ["messageIds", "string"], ["addLabelIds", "string"], ["removeLabelIds", "string"]], outputKeys: ["updatedMessageIds"] },
|
|
3113
|
-
"updateGoogleCalendarEvent": { fields: [["eventId", "string"]], outputKeys: ["eventId", "htmlLink"] },
|
|
3114
|
-
"updateGoogleDoc": { fields: [["documentId", "string"], ["text", "string"], ["textType", ["plain", "html", "markdown"]], ["operationType", ["addToTop", "addToBottom", "overwrite"]]], outputKeys: ["documentUrl"] },
|
|
3115
|
-
"updateGoogleSheet": { fields: [["text", "string"], ["spreadsheetId", "string"], ["range", "string"], ["operationType", ["addToBottom", "overwrite", "range"]]], outputKeys: ["spreadsheetUrl"] },
|
|
3116
|
-
"uploadDataSourceDocument": { fields: [["dataSourceId", "string"], ["file", "string"], ["fileName", "string"]], outputKeys: [] },
|
|
3117
|
-
"upscaleImage": { fields: [["imageUrl", "string"], ["targetResolution", ["2k", "4k", "8k"]], ["engine", ["standard", "pro"]]], outputKeys: ["imageUrl"] },
|
|
3118
|
-
"upscaleVideo": { fields: [["videoUrl", "string"], ["targetResolution", ["720p", "1080p", "2K", "4K"]], ["engine", ["standard", "pro", "ultimate", "flashvsr", "seedance", "seedvr2", "runwayml/upscale-v1"]]], outputKeys: ["videoUrl"] },
|
|
3119
|
-
"userMessage": { fields: [["message", "string"]], outputKeys: ["content"] },
|
|
3120
|
-
"videoFaceSwap": { fields: [["videoUrl", "string"], ["faceImageUrl", "string"], ["targetIndex", "number"], ["engine", "string"]], outputKeys: ["videoUrl"] },
|
|
3121
|
-
"videoRemoveBackground": { fields: [["videoUrl", "string"], ["newBackground", ["transparent", "image"]], ["engine", "string"]], outputKeys: ["videoUrl"] },
|
|
3122
|
-
"videoRemoveWatermark": { fields: [["videoUrl", "string"], ["engine", "string"]], outputKeys: ["videoUrl"] },
|
|
3123
|
-
"watermarkImage": { fields: [["imageUrl", "string"], ["watermarkImageUrl", "string"], ["corner", ["top-left", "top-right", "bottom-left", "bottom-right"]], ["paddingPx", "number"], ["widthPx", "number"]], outputKeys: ["imageUrl"] },
|
|
3124
|
-
"watermarkVideo": { fields: [["videoUrl", "string"], ["imageUrl", "string"], ["corner", ["top-left", "top-right", "bottom-left", "bottom-right"]], ["paddingPx", "number"], ["widthPx", "number"]], outputKeys: ["videoUrl"] }
|
|
3125
|
-
};
|
|
3126
|
-
var blockTypeAliases = {
|
|
3127
|
-
"userMessage": "generateText",
|
|
3128
|
-
"generatePdf": "generateAsset"
|
|
3129
|
-
};
|
|
3130
|
-
|
|
3131
2069
|
// src/generated/metadata.ts
|
|
3132
2070
|
var stepMetadata = {
|
|
3133
2071
|
"activeCampaignAddNote": {
|
|
@@ -4289,6 +3227,1183 @@ var stepMetadata = {
|
|
|
4289
3227
|
}
|
|
4290
3228
|
};
|
|
4291
3229
|
|
|
3230
|
+
// src/client.ts
|
|
3231
|
+
var DEFAULT_BASE_URL = "https://v1.mindstudio-api.com";
|
|
3232
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
3233
|
+
var MindStudioAgent = class {
|
|
3234
|
+
/** @internal */
|
|
3235
|
+
_httpConfig;
|
|
3236
|
+
/** @internal */
|
|
3237
|
+
_reuseThreadId;
|
|
3238
|
+
/** @internal */
|
|
3239
|
+
_threadId;
|
|
3240
|
+
/** @internal Stream ID for SSE token streaming. Set by sandbox via STREAM_ID env var. */
|
|
3241
|
+
_streamId;
|
|
3242
|
+
// ---- App context (db + auth) ----
|
|
3243
|
+
/**
|
|
3244
|
+
* @internal App ID for context resolution. Resolved from:
|
|
3245
|
+
* constructor appId → MINDSTUDIO_APP_ID env → sandbox globals →
|
|
3246
|
+
* auto-detected from first executeStep response header.
|
|
3247
|
+
*/
|
|
3248
|
+
_appId;
|
|
3249
|
+
/**
|
|
3250
|
+
* @internal Cached app context (auth + databases). Populated by
|
|
3251
|
+
* ensureContext() and cached for the lifetime of the instance.
|
|
3252
|
+
*/
|
|
3253
|
+
_context;
|
|
3254
|
+
/**
|
|
3255
|
+
* @internal Deduplication promise for ensureContext(). Ensures only one
|
|
3256
|
+
* context fetch is in-flight at a time, even if multiple db/auth
|
|
3257
|
+
* operations trigger it concurrently.
|
|
3258
|
+
*/
|
|
3259
|
+
_contextPromise;
|
|
3260
|
+
/** @internal Cached AuthContext instance, created during context hydration. */
|
|
3261
|
+
_auth;
|
|
3262
|
+
/** @internal Cached Db namespace instance, created during context hydration. */
|
|
3263
|
+
_db;
|
|
3264
|
+
/** @internal Auth type — 'internal' for CALLBACK_TOKEN (managed mode), 'apiKey' otherwise. */
|
|
3265
|
+
_authType;
|
|
3266
|
+
/**
|
|
3267
|
+
* @internal Resolve the current auth token. For internal (CALLBACK_TOKEN)
|
|
3268
|
+
* auth, re-reads the env var each time so that long-lived singleton
|
|
3269
|
+
* instances pick up token rotations from the host process.
|
|
3270
|
+
*/
|
|
3271
|
+
get _token() {
|
|
3272
|
+
if (this._authType === "internal" && process.env.CALLBACK_TOKEN) {
|
|
3273
|
+
return process.env.CALLBACK_TOKEN;
|
|
3274
|
+
}
|
|
3275
|
+
return this._httpConfig.token;
|
|
3276
|
+
}
|
|
3277
|
+
constructor(options = {}) {
|
|
3278
|
+
const config = loadConfig();
|
|
3279
|
+
const { token, authType } = resolveToken(options.apiKey, config);
|
|
3280
|
+
const baseUrl = options.baseUrl ?? process.env.MINDSTUDIO_BASE_URL ?? process.env.REMOTE_HOSTNAME ?? config.baseUrl ?? DEFAULT_BASE_URL;
|
|
3281
|
+
this._reuseThreadId = options.reuseThreadId ?? /^(true|1)$/i.test(process.env.MINDSTUDIO_REUSE_THREAD_ID ?? "");
|
|
3282
|
+
this._appId = options.appId ?? process.env.MINDSTUDIO_APP_ID ?? void 0;
|
|
3283
|
+
this._authType = authType;
|
|
3284
|
+
this._httpConfig = {
|
|
3285
|
+
baseUrl,
|
|
3286
|
+
token,
|
|
3287
|
+
rateLimiter: new RateLimiter(authType),
|
|
3288
|
+
maxRetries: options.maxRetries ?? DEFAULT_MAX_RETRIES
|
|
3289
|
+
};
|
|
3290
|
+
if (authType === "internal") {
|
|
3291
|
+
this._trySandboxHydration();
|
|
3292
|
+
}
|
|
3293
|
+
this._streamId = process.env.STREAM_ID ?? void 0;
|
|
3294
|
+
}
|
|
3295
|
+
/**
|
|
3296
|
+
* Execute any step by its type name. This is the low-level method that all
|
|
3297
|
+
* typed step methods delegate to. Use it as an escape hatch for step types
|
|
3298
|
+
* not yet covered by the generated methods.
|
|
3299
|
+
*
|
|
3300
|
+
* ```ts
|
|
3301
|
+
* const result = await agent.executeStep("generateImage", { prompt: "hello", mode: "background" });
|
|
3302
|
+
* ```
|
|
3303
|
+
*/
|
|
3304
|
+
async executeStep(stepType, step, options) {
|
|
3305
|
+
if (options?.onLog) {
|
|
3306
|
+
return this._executeStepStreaming(
|
|
3307
|
+
stepType,
|
|
3308
|
+
step,
|
|
3309
|
+
options
|
|
3310
|
+
);
|
|
3311
|
+
}
|
|
3312
|
+
const threadId = options?.threadId ?? (this._reuseThreadId ? this._threadId : void 0);
|
|
3313
|
+
const { data, headers } = await request(this._httpConfig, "POST", `/steps/${stepType}/execute`, {
|
|
3314
|
+
step,
|
|
3315
|
+
...options?.appId != null && { appId: options.appId },
|
|
3316
|
+
...threadId != null && { threadId },
|
|
3317
|
+
...this._streamId != null && { streamId: this._streamId }
|
|
3318
|
+
});
|
|
3319
|
+
let output;
|
|
3320
|
+
if (data.output != null) {
|
|
3321
|
+
output = data.output;
|
|
3322
|
+
} else if (data.outputUrl) {
|
|
3323
|
+
const res = await fetch(data.outputUrl);
|
|
3324
|
+
if (!res.ok) {
|
|
3325
|
+
throw new MindStudioError(
|
|
3326
|
+
`Failed to fetch output from S3: ${res.status} ${res.statusText}`,
|
|
3327
|
+
"output_fetch_error",
|
|
3328
|
+
res.status
|
|
3329
|
+
);
|
|
3330
|
+
}
|
|
3331
|
+
const envelope = await res.json();
|
|
3332
|
+
output = envelope.value;
|
|
3333
|
+
} else {
|
|
3334
|
+
output = void 0;
|
|
3335
|
+
}
|
|
3336
|
+
const returnedThreadId = headers.get("x-mindstudio-thread-id") ?? "";
|
|
3337
|
+
if (this._reuseThreadId && returnedThreadId) {
|
|
3338
|
+
this._threadId = returnedThreadId;
|
|
3339
|
+
}
|
|
3340
|
+
const returnedAppId = headers.get("x-mindstudio-app-id");
|
|
3341
|
+
if (!this._appId && returnedAppId) {
|
|
3342
|
+
this._appId = returnedAppId;
|
|
3343
|
+
}
|
|
3344
|
+
const remaining = headers.get("x-ratelimit-remaining");
|
|
3345
|
+
const billingCost = headers.get("x-mindstudio-billing-cost");
|
|
3346
|
+
const billingEvents = headers.get("x-mindstudio-billing-events");
|
|
3347
|
+
return {
|
|
3348
|
+
...output,
|
|
3349
|
+
$appId: headers.get("x-mindstudio-app-id") ?? "",
|
|
3350
|
+
$threadId: returnedThreadId,
|
|
3351
|
+
$rateLimitRemaining: remaining != null ? parseInt(remaining, 10) : void 0,
|
|
3352
|
+
$billingCost: billingCost != null ? parseFloat(billingCost) : void 0,
|
|
3353
|
+
$billingEvents: billingEvents != null ? JSON.parse(billingEvents) : void 0
|
|
3354
|
+
};
|
|
3355
|
+
}
|
|
3356
|
+
/**
|
|
3357
|
+
* @internal Streaming step execution — sends `Accept: text/event-stream`
|
|
3358
|
+
* and parses SSE events for real-time debug logs.
|
|
3359
|
+
*/
|
|
3360
|
+
async _executeStepStreaming(stepType, step, options) {
|
|
3361
|
+
const threadId = options.threadId ?? (this._reuseThreadId ? this._threadId : void 0);
|
|
3362
|
+
const url = `${this._httpConfig.baseUrl}/developer/v2/steps/${stepType}/execute`;
|
|
3363
|
+
const body = {
|
|
3364
|
+
step,
|
|
3365
|
+
...options.appId != null && { appId: options.appId },
|
|
3366
|
+
...threadId != null && { threadId },
|
|
3367
|
+
...this._streamId != null && { streamId: this._streamId }
|
|
3368
|
+
};
|
|
3369
|
+
await this._httpConfig.rateLimiter.acquire();
|
|
3370
|
+
let res;
|
|
3371
|
+
try {
|
|
3372
|
+
res = await fetch(url, {
|
|
3373
|
+
method: "POST",
|
|
3374
|
+
headers: {
|
|
3375
|
+
Authorization: `Bearer ${this._token}`,
|
|
3376
|
+
"Content-Type": "application/json",
|
|
3377
|
+
"User-Agent": "@mindstudio-ai/agent",
|
|
3378
|
+
Accept: "text/event-stream"
|
|
3379
|
+
},
|
|
3380
|
+
body: JSON.stringify(body)
|
|
3381
|
+
});
|
|
3382
|
+
} catch (err) {
|
|
3383
|
+
this._httpConfig.rateLimiter.release();
|
|
3384
|
+
throw err;
|
|
3385
|
+
}
|
|
3386
|
+
this._httpConfig.rateLimiter.updateFromHeaders(res.headers);
|
|
3387
|
+
if (!res.ok) {
|
|
3388
|
+
this._httpConfig.rateLimiter.release();
|
|
3389
|
+
const errorBody = await res.json().catch(() => ({}));
|
|
3390
|
+
throw new MindStudioError(
|
|
3391
|
+
errorBody.message || `${res.status} ${res.statusText}`,
|
|
3392
|
+
errorBody.code || "api_error",
|
|
3393
|
+
res.status,
|
|
3394
|
+
errorBody
|
|
3395
|
+
);
|
|
3396
|
+
}
|
|
3397
|
+
const headers = res.headers;
|
|
3398
|
+
try {
|
|
3399
|
+
const reader = res.body.getReader();
|
|
3400
|
+
const decoder = new TextDecoder();
|
|
3401
|
+
let buffer = "";
|
|
3402
|
+
let doneEvent = null;
|
|
3403
|
+
while (true) {
|
|
3404
|
+
const { done, value } = await reader.read();
|
|
3405
|
+
if (done) break;
|
|
3406
|
+
buffer += decoder.decode(value, { stream: true });
|
|
3407
|
+
const lines = buffer.split("\n");
|
|
3408
|
+
buffer = lines.pop() ?? "";
|
|
3409
|
+
for (const line of lines) {
|
|
3410
|
+
if (!line.startsWith("data: ")) continue;
|
|
3411
|
+
try {
|
|
3412
|
+
const event = JSON.parse(line.slice(6));
|
|
3413
|
+
if (event.type === "log") {
|
|
3414
|
+
options.onLog({
|
|
3415
|
+
value: event.value,
|
|
3416
|
+
tag: event.tag,
|
|
3417
|
+
ts: event.ts
|
|
3418
|
+
});
|
|
3419
|
+
} else if (event.type === "done") {
|
|
3420
|
+
doneEvent = {
|
|
3421
|
+
output: event.output,
|
|
3422
|
+
outputUrl: event.outputUrl,
|
|
3423
|
+
billingCost: event.billingCost,
|
|
3424
|
+
billingEvents: event.billingEvents
|
|
3425
|
+
};
|
|
3426
|
+
} else if (event.type === "error") {
|
|
3427
|
+
throw new MindStudioError(
|
|
3428
|
+
event.error || "Step execution failed",
|
|
3429
|
+
"step_error",
|
|
3430
|
+
500
|
|
3431
|
+
);
|
|
3432
|
+
}
|
|
3433
|
+
} catch (err) {
|
|
3434
|
+
if (err instanceof MindStudioError) throw err;
|
|
3435
|
+
}
|
|
3436
|
+
}
|
|
3437
|
+
}
|
|
3438
|
+
if (buffer.startsWith("data: ")) {
|
|
3439
|
+
try {
|
|
3440
|
+
const event = JSON.parse(buffer.slice(6));
|
|
3441
|
+
if (event.type === "done") {
|
|
3442
|
+
doneEvent = {
|
|
3443
|
+
output: event.output,
|
|
3444
|
+
outputUrl: event.outputUrl,
|
|
3445
|
+
billingCost: event.billingCost,
|
|
3446
|
+
billingEvents: event.billingEvents
|
|
3447
|
+
};
|
|
3448
|
+
} else if (event.type === "error") {
|
|
3449
|
+
throw new MindStudioError(
|
|
3450
|
+
event.error || "Step execution failed",
|
|
3451
|
+
"step_error",
|
|
3452
|
+
500
|
|
3453
|
+
);
|
|
3454
|
+
} else if (event.type === "log") {
|
|
3455
|
+
options.onLog({
|
|
3456
|
+
value: event.value,
|
|
3457
|
+
tag: event.tag,
|
|
3458
|
+
ts: event.ts
|
|
3459
|
+
});
|
|
3460
|
+
}
|
|
3461
|
+
} catch (err) {
|
|
3462
|
+
if (err instanceof MindStudioError) throw err;
|
|
3463
|
+
}
|
|
3464
|
+
}
|
|
3465
|
+
if (!doneEvent) {
|
|
3466
|
+
throw new MindStudioError(
|
|
3467
|
+
"Stream ended without a done event",
|
|
3468
|
+
"stream_error",
|
|
3469
|
+
500
|
|
3470
|
+
);
|
|
3471
|
+
}
|
|
3472
|
+
let output;
|
|
3473
|
+
if (doneEvent.output != null) {
|
|
3474
|
+
output = doneEvent.output;
|
|
3475
|
+
} else if (doneEvent.outputUrl) {
|
|
3476
|
+
const s3Res = await fetch(doneEvent.outputUrl);
|
|
3477
|
+
if (!s3Res.ok) {
|
|
3478
|
+
throw new MindStudioError(
|
|
3479
|
+
`Failed to fetch output from S3: ${s3Res.status} ${s3Res.statusText}`,
|
|
3480
|
+
"output_fetch_error",
|
|
3481
|
+
s3Res.status
|
|
3482
|
+
);
|
|
3483
|
+
}
|
|
3484
|
+
const envelope = await s3Res.json();
|
|
3485
|
+
output = envelope.value;
|
|
3486
|
+
} else {
|
|
3487
|
+
output = void 0;
|
|
3488
|
+
}
|
|
3489
|
+
const returnedThreadId = headers.get("x-mindstudio-thread-id") ?? "";
|
|
3490
|
+
if (this._reuseThreadId && returnedThreadId) {
|
|
3491
|
+
this._threadId = returnedThreadId;
|
|
3492
|
+
}
|
|
3493
|
+
const returnedAppId = headers.get("x-mindstudio-app-id");
|
|
3494
|
+
if (!this._appId && returnedAppId) {
|
|
3495
|
+
this._appId = returnedAppId;
|
|
3496
|
+
}
|
|
3497
|
+
const remaining = headers.get("x-ratelimit-remaining");
|
|
3498
|
+
return {
|
|
3499
|
+
...output,
|
|
3500
|
+
$appId: headers.get("x-mindstudio-app-id") ?? "",
|
|
3501
|
+
$threadId: returnedThreadId,
|
|
3502
|
+
$rateLimitRemaining: remaining != null ? parseInt(remaining, 10) : void 0,
|
|
3503
|
+
$billingCost: doneEvent.billingCost,
|
|
3504
|
+
$billingEvents: doneEvent.billingEvents
|
|
3505
|
+
};
|
|
3506
|
+
} finally {
|
|
3507
|
+
this._httpConfig.rateLimiter.release();
|
|
3508
|
+
}
|
|
3509
|
+
}
|
|
3510
|
+
/**
|
|
3511
|
+
* Execute multiple steps in parallel in a single request.
|
|
3512
|
+
*
|
|
3513
|
+
* All steps run in parallel on the server. Results are returned in the same
|
|
3514
|
+
* order as the input. Individual step failures do not affect other steps —
|
|
3515
|
+
* partial success is possible.
|
|
3516
|
+
*
|
|
3517
|
+
* ```ts
|
|
3518
|
+
* const { results } = await agent.executeStepBatch([
|
|
3519
|
+
* { stepType: 'generateImage', step: { prompt: 'a sunset' } },
|
|
3520
|
+
* { stepType: 'textToSpeech', step: { text: 'Hello world' } },
|
|
3521
|
+
* ]);
|
|
3522
|
+
* ```
|
|
3523
|
+
*/
|
|
3524
|
+
async executeStepBatch(steps, options) {
|
|
3525
|
+
const threadId = options?.threadId ?? (this._reuseThreadId ? this._threadId : void 0);
|
|
3526
|
+
const { data } = await request(this._httpConfig, "POST", "/steps/execute-batch", {
|
|
3527
|
+
steps: steps.map((s) => ({ ...s, stepType: resolveStepType(s.stepType) })),
|
|
3528
|
+
...options?.appId != null && { appId: options.appId },
|
|
3529
|
+
...threadId != null && { threadId }
|
|
3530
|
+
});
|
|
3531
|
+
const results = await Promise.all(
|
|
3532
|
+
data.results.map(async (r) => {
|
|
3533
|
+
if (r.output != null) {
|
|
3534
|
+
return {
|
|
3535
|
+
stepType: r.stepType,
|
|
3536
|
+
output: r.output,
|
|
3537
|
+
billingCost: r.billingCost,
|
|
3538
|
+
error: r.error
|
|
3539
|
+
};
|
|
3540
|
+
}
|
|
3541
|
+
if (r.outputUrl) {
|
|
3542
|
+
const res = await fetch(r.outputUrl);
|
|
3543
|
+
if (!res.ok) {
|
|
3544
|
+
return {
|
|
3545
|
+
stepType: r.stepType,
|
|
3546
|
+
error: `Failed to fetch output from S3: ${res.status} ${res.statusText}`
|
|
3547
|
+
};
|
|
3548
|
+
}
|
|
3549
|
+
const envelope = await res.json();
|
|
3550
|
+
return {
|
|
3551
|
+
stepType: r.stepType,
|
|
3552
|
+
output: envelope.value,
|
|
3553
|
+
billingCost: r.billingCost
|
|
3554
|
+
};
|
|
3555
|
+
}
|
|
3556
|
+
return {
|
|
3557
|
+
stepType: r.stepType,
|
|
3558
|
+
billingCost: r.billingCost,
|
|
3559
|
+
error: r.error
|
|
3560
|
+
};
|
|
3561
|
+
})
|
|
3562
|
+
);
|
|
3563
|
+
if (this._reuseThreadId && data.threadId) {
|
|
3564
|
+
this._threadId = data.threadId;
|
|
3565
|
+
}
|
|
3566
|
+
return {
|
|
3567
|
+
results,
|
|
3568
|
+
totalBillingCost: data.totalBillingCost,
|
|
3569
|
+
appId: data.appId,
|
|
3570
|
+
threadId: data.threadId
|
|
3571
|
+
};
|
|
3572
|
+
}
|
|
3573
|
+
/**
|
|
3574
|
+
* Get the authenticated user's identity and organization info.
|
|
3575
|
+
*
|
|
3576
|
+
* ```ts
|
|
3577
|
+
* const info = await agent.getUserInfo();
|
|
3578
|
+
* console.log(info.displayName, info.organizationName);
|
|
3579
|
+
* ```
|
|
3580
|
+
*/
|
|
3581
|
+
async getUserInfo() {
|
|
3582
|
+
const { data } = await request(
|
|
3583
|
+
this._httpConfig,
|
|
3584
|
+
"GET",
|
|
3585
|
+
"/account/userinfo"
|
|
3586
|
+
);
|
|
3587
|
+
return data;
|
|
3588
|
+
}
|
|
3589
|
+
/**
|
|
3590
|
+
* List all pre-built agents in the organization.
|
|
3591
|
+
*
|
|
3592
|
+
* ```ts
|
|
3593
|
+
* const { apps } = await agent.listAgents();
|
|
3594
|
+
* for (const app of apps) console.log(app.name, app.id);
|
|
3595
|
+
* ```
|
|
3596
|
+
*/
|
|
3597
|
+
async listAgents() {
|
|
3598
|
+
const { data } = await request(
|
|
3599
|
+
this._httpConfig,
|
|
3600
|
+
"GET",
|
|
3601
|
+
"/agents/load"
|
|
3602
|
+
);
|
|
3603
|
+
return data;
|
|
3604
|
+
}
|
|
3605
|
+
/**
|
|
3606
|
+
* Run a pre-built agent and wait for the result.
|
|
3607
|
+
*
|
|
3608
|
+
* Uses async polling internally — the request returns immediately with a
|
|
3609
|
+
* callback token, then polls until the run completes or fails.
|
|
3610
|
+
*
|
|
3611
|
+
* ```ts
|
|
3612
|
+
* const result = await agent.runAgent({
|
|
3613
|
+
* appId: 'your-agent-id',
|
|
3614
|
+
* variables: { query: 'hello' },
|
|
3615
|
+
* });
|
|
3616
|
+
* console.log(result.result);
|
|
3617
|
+
* ```
|
|
3618
|
+
*/
|
|
3619
|
+
async runAgent(options) {
|
|
3620
|
+
const pollInterval = options.pollIntervalMs ?? 1e3;
|
|
3621
|
+
const { data } = await request(this._httpConfig, "POST", "/agents/run", {
|
|
3622
|
+
appId: options.appId,
|
|
3623
|
+
async: true,
|
|
3624
|
+
...options.variables != null && { variables: options.variables },
|
|
3625
|
+
...options.workflow != null && { workflow: options.workflow },
|
|
3626
|
+
...options.version != null && { version: options.version },
|
|
3627
|
+
...options.includeBillingCost != null && {
|
|
3628
|
+
includeBillingCost: options.includeBillingCost
|
|
3629
|
+
},
|
|
3630
|
+
...options.metadata != null && { metadata: options.metadata }
|
|
3631
|
+
});
|
|
3632
|
+
const token = data.callbackToken;
|
|
3633
|
+
const pollUrl = `${this._httpConfig.baseUrl}/developer/v2/agents/run/poll/${token}`;
|
|
3634
|
+
while (true) {
|
|
3635
|
+
await sleep2(pollInterval);
|
|
3636
|
+
const res = await fetch(pollUrl, {
|
|
3637
|
+
headers: { "User-Agent": "@mindstudio-ai/agent" }
|
|
3638
|
+
});
|
|
3639
|
+
if (res.status === 404) {
|
|
3640
|
+
throw new MindStudioError(
|
|
3641
|
+
"Poll token not found or expired.",
|
|
3642
|
+
"poll_token_expired",
|
|
3643
|
+
404
|
|
3644
|
+
);
|
|
3645
|
+
}
|
|
3646
|
+
if (!res.ok) {
|
|
3647
|
+
const errorBody = await res.json().catch(() => ({}));
|
|
3648
|
+
throw new MindStudioError(
|
|
3649
|
+
errorBody.message ?? errorBody.error ?? `Poll request failed: ${res.status} ${res.statusText}`,
|
|
3650
|
+
errorBody.code ?? "poll_error",
|
|
3651
|
+
res.status,
|
|
3652
|
+
errorBody
|
|
3653
|
+
);
|
|
3654
|
+
}
|
|
3655
|
+
const poll = await res.json();
|
|
3656
|
+
if (poll.status === "pending") continue;
|
|
3657
|
+
if (poll.status === "error") {
|
|
3658
|
+
throw new MindStudioError(
|
|
3659
|
+
poll.error ?? "Agent run failed.",
|
|
3660
|
+
"agent_run_error",
|
|
3661
|
+
500
|
|
3662
|
+
);
|
|
3663
|
+
}
|
|
3664
|
+
return poll.result;
|
|
3665
|
+
}
|
|
3666
|
+
}
|
|
3667
|
+
/** @internal Used by generated action methods. */
|
|
3668
|
+
_request(method, path, body) {
|
|
3669
|
+
return request(this._httpConfig, method, path, body);
|
|
3670
|
+
}
|
|
3671
|
+
// -------------------------------------------------------------------------
|
|
3672
|
+
// Helper methods — models
|
|
3673
|
+
// -------------------------------------------------------------------------
|
|
3674
|
+
/** List all available AI models. */
|
|
3675
|
+
async listModels() {
|
|
3676
|
+
const { data } = await request(
|
|
3677
|
+
this._httpConfig,
|
|
3678
|
+
"GET",
|
|
3679
|
+
"/helpers/models"
|
|
3680
|
+
);
|
|
3681
|
+
return data;
|
|
3682
|
+
}
|
|
3683
|
+
/** List AI models filtered by type. */
|
|
3684
|
+
async listModelsByType(modelType) {
|
|
3685
|
+
const { data } = await request(
|
|
3686
|
+
this._httpConfig,
|
|
3687
|
+
"GET",
|
|
3688
|
+
`/helpers/models/${modelType}`
|
|
3689
|
+
);
|
|
3690
|
+
return data;
|
|
3691
|
+
}
|
|
3692
|
+
/** List all available AI models (summary). Returns only id, name, type, and tags. */
|
|
3693
|
+
async listModelsSummary() {
|
|
3694
|
+
const { data } = await request(
|
|
3695
|
+
this._httpConfig,
|
|
3696
|
+
"GET",
|
|
3697
|
+
"/helpers/models-summary"
|
|
3698
|
+
);
|
|
3699
|
+
return data;
|
|
3700
|
+
}
|
|
3701
|
+
/** List AI models (summary) filtered by type. */
|
|
3702
|
+
async listModelsSummaryByType(modelType) {
|
|
3703
|
+
const { data } = await request(
|
|
3704
|
+
this._httpConfig,
|
|
3705
|
+
"GET",
|
|
3706
|
+
`/helpers/models-summary/${modelType}`
|
|
3707
|
+
);
|
|
3708
|
+
return data;
|
|
3709
|
+
}
|
|
3710
|
+
// -------------------------------------------------------------------------
|
|
3711
|
+
// Helper methods — OAuth connectors & connections
|
|
3712
|
+
// -------------------------------------------------------------------------
|
|
3713
|
+
/**
|
|
3714
|
+
* List available OAuth connector services (Slack, Google, HubSpot, etc.).
|
|
3715
|
+
*
|
|
3716
|
+
* These are third-party integrations from the MindStudio Connector Registry.
|
|
3717
|
+
* For most tasks, use actions directly instead.
|
|
3718
|
+
*/
|
|
3719
|
+
async listConnectors() {
|
|
3720
|
+
const { data } = await request(
|
|
3721
|
+
this._httpConfig,
|
|
3722
|
+
"GET",
|
|
3723
|
+
"/helpers/connectors"
|
|
3724
|
+
);
|
|
3725
|
+
return data;
|
|
3726
|
+
}
|
|
3727
|
+
/** Get details for a single OAuth connector service. */
|
|
3728
|
+
async getConnector(serviceId) {
|
|
3729
|
+
const { data } = await request(
|
|
3730
|
+
this._httpConfig,
|
|
3731
|
+
"GET",
|
|
3732
|
+
`/helpers/connectors/${serviceId}`
|
|
3733
|
+
);
|
|
3734
|
+
return data;
|
|
3735
|
+
}
|
|
3736
|
+
/** Get the full configuration for an OAuth connector action, including input fields. */
|
|
3737
|
+
async getConnectorAction(serviceId, actionId) {
|
|
3738
|
+
const { data } = await request(
|
|
3739
|
+
this._httpConfig,
|
|
3740
|
+
"GET",
|
|
3741
|
+
`/helpers/connectors/${serviceId}/${actionId}`
|
|
3742
|
+
);
|
|
3743
|
+
return data;
|
|
3744
|
+
}
|
|
3745
|
+
/** List OAuth connections for the organization. These are authenticated third-party service links. */
|
|
3746
|
+
async listConnections() {
|
|
3747
|
+
const { data } = await request(
|
|
3748
|
+
this._httpConfig,
|
|
3749
|
+
"GET",
|
|
3750
|
+
"/helpers/connections"
|
|
3751
|
+
);
|
|
3752
|
+
return data;
|
|
3753
|
+
}
|
|
3754
|
+
// -------------------------------------------------------------------------
|
|
3755
|
+
// Helper methods — cost estimation
|
|
3756
|
+
// -------------------------------------------------------------------------
|
|
3757
|
+
/** Estimate the cost of executing an action before running it. */
|
|
3758
|
+
async estimateStepCost(stepType, step, options) {
|
|
3759
|
+
const { data } = await request(this._httpConfig, "POST", "/helpers/step-cost-estimate", {
|
|
3760
|
+
step: { type: resolveStepType(stepType), ...step },
|
|
3761
|
+
...options
|
|
3762
|
+
});
|
|
3763
|
+
return data;
|
|
3764
|
+
}
|
|
3765
|
+
// -------------------------------------------------------------------------
|
|
3766
|
+
// Streaming
|
|
3767
|
+
// -------------------------------------------------------------------------
|
|
3768
|
+
/**
|
|
3769
|
+
* Send a stream chunk to the caller via SSE.
|
|
3770
|
+
*
|
|
3771
|
+
* When invoked from a method that was called with `stream: true`, chunks
|
|
3772
|
+
* are delivered in real-time as Server-Sent Events. When there is no active
|
|
3773
|
+
* stream (no `STREAM_ID`), calls are silently ignored — so it's safe to
|
|
3774
|
+
* call unconditionally.
|
|
3775
|
+
*
|
|
3776
|
+
* Accepts strings (sent as `type: 'token'`) or structured data (sent as
|
|
3777
|
+
* `type: 'data'`). The caller receives each chunk as an SSE event.
|
|
3778
|
+
*
|
|
3779
|
+
* @example
|
|
3780
|
+
* ```ts
|
|
3781
|
+
* // Stream text tokens
|
|
3782
|
+
* await agent.stream('Processing item 1...');
|
|
3783
|
+
*
|
|
3784
|
+
* // Stream structured data
|
|
3785
|
+
* await agent.stream({ progress: 50, currentItem: 'abc' });
|
|
3786
|
+
* ```
|
|
3787
|
+
*/
|
|
3788
|
+
stream = async (data) => {
|
|
3789
|
+
if (!this._streamId) return;
|
|
3790
|
+
const url = `${this._httpConfig.baseUrl}/_internal/v2/stream-chunk`;
|
|
3791
|
+
const body = typeof data === "string" ? { streamId: this._streamId, type: "token", text: data } : { streamId: this._streamId, type: "data", data };
|
|
3792
|
+
const res = await fetch(url, {
|
|
3793
|
+
method: "POST",
|
|
3794
|
+
headers: {
|
|
3795
|
+
"Content-Type": "application/json",
|
|
3796
|
+
Authorization: this._token
|
|
3797
|
+
},
|
|
3798
|
+
body: JSON.stringify(body)
|
|
3799
|
+
});
|
|
3800
|
+
if (!res.ok) {
|
|
3801
|
+
const text = await res.text().catch(() => "");
|
|
3802
|
+
console.warn(`[mindstudio] stream chunk failed: ${res.status} ${text}`);
|
|
3803
|
+
}
|
|
3804
|
+
};
|
|
3805
|
+
// -------------------------------------------------------------------------
|
|
3806
|
+
// db + auth namespaces
|
|
3807
|
+
// -------------------------------------------------------------------------
|
|
3808
|
+
/**
|
|
3809
|
+
* The `auth` namespace — synchronous role-based access control.
|
|
3810
|
+
*
|
|
3811
|
+
* Provides the current user's identity and roles. All methods are
|
|
3812
|
+
* synchronous since the role map is preloaded during context hydration.
|
|
3813
|
+
*
|
|
3814
|
+
* **Important**: Context must be hydrated before accessing `auth`.
|
|
3815
|
+
* - Inside the MindStudio sandbox: automatic (populated from globals)
|
|
3816
|
+
* - Outside the sandbox: call `await agent.ensureContext()` first,
|
|
3817
|
+
* or access `auth` after any `db` operation (which auto-hydrates)
|
|
3818
|
+
*
|
|
3819
|
+
* @throws {MindStudioError} if context has not been hydrated yet
|
|
3820
|
+
*
|
|
3821
|
+
* @example
|
|
3822
|
+
* ```ts
|
|
3823
|
+
* await agent.ensureContext();
|
|
3824
|
+
* agent.auth.requireRole(Roles.admin);
|
|
3825
|
+
* const admins = agent.auth.getUsersByRole(Roles.admin);
|
|
3826
|
+
* ```
|
|
3827
|
+
*/
|
|
3828
|
+
get auth() {
|
|
3829
|
+
if (!this._auth) {
|
|
3830
|
+
this._trySandboxHydration();
|
|
3831
|
+
}
|
|
3832
|
+
if (!this._auth) {
|
|
3833
|
+
throw new MindStudioError(
|
|
3834
|
+
"Auth context not yet loaded. Call `await agent.ensureContext()` or perform any db operation first (which auto-hydrates context). Inside the MindStudio sandbox, context is loaded automatically.",
|
|
3835
|
+
"context_not_loaded",
|
|
3836
|
+
400
|
|
3837
|
+
);
|
|
3838
|
+
}
|
|
3839
|
+
return this._auth;
|
|
3840
|
+
}
|
|
3841
|
+
/**
|
|
3842
|
+
* The `db` namespace — chainable collection API over managed databases.
|
|
3843
|
+
*
|
|
3844
|
+
* Use `db.defineTable<T>(name)` to get a typed Table<T>, then call
|
|
3845
|
+
* collection methods (filter, sortBy, push, update, etc.) on it.
|
|
3846
|
+
*
|
|
3847
|
+
* Context is auto-hydrated on first query execution — you can safely
|
|
3848
|
+
* call `defineTable()` at module scope without triggering any HTTP.
|
|
3849
|
+
*
|
|
3850
|
+
* @example
|
|
3851
|
+
* ```ts
|
|
3852
|
+
* const Orders = agent.db.defineTable<Order>('orders');
|
|
3853
|
+
* const active = await Orders.filter(o => o.status === 'active').take(10);
|
|
3854
|
+
* ```
|
|
3855
|
+
*/
|
|
3856
|
+
get db() {
|
|
3857
|
+
if (!this._db) {
|
|
3858
|
+
this._trySandboxHydration();
|
|
3859
|
+
}
|
|
3860
|
+
if (this._db) return this._db;
|
|
3861
|
+
return this._createLazyDb();
|
|
3862
|
+
}
|
|
3863
|
+
/**
|
|
3864
|
+
* Hydrate the app context (auth + database metadata). This must be
|
|
3865
|
+
* called before using `auth` synchronously. For `db`, hydration happens
|
|
3866
|
+
* automatically on first query.
|
|
3867
|
+
*
|
|
3868
|
+
* Context is fetched once and cached for the instance's lifetime.
|
|
3869
|
+
* Calling `ensureContext()` multiple times is safe (no-op after first).
|
|
3870
|
+
*
|
|
3871
|
+
* Context sources (checked in order):
|
|
3872
|
+
* 1. Sandbox globals (`globalThis.ai.auth`, `globalThis.ai.databases`)
|
|
3873
|
+
* 2. HTTP: `GET /developer/v2/helpers/app-context?appId={appId}`
|
|
3874
|
+
*
|
|
3875
|
+
* @throws {MindStudioError} if no `appId` is available
|
|
3876
|
+
*
|
|
3877
|
+
* @example
|
|
3878
|
+
* ```ts
|
|
3879
|
+
* await agent.ensureContext();
|
|
3880
|
+
* // auth is now available synchronously
|
|
3881
|
+
* agent.auth.requireRole(Roles.admin);
|
|
3882
|
+
* ```
|
|
3883
|
+
*/
|
|
3884
|
+
async ensureContext() {
|
|
3885
|
+
if (this._context) return;
|
|
3886
|
+
if (!this._contextPromise) {
|
|
3887
|
+
this._contextPromise = this._hydrateContext();
|
|
3888
|
+
}
|
|
3889
|
+
await this._contextPromise;
|
|
3890
|
+
}
|
|
3891
|
+
/**
|
|
3892
|
+
* @internal Fetch and cache app context, then create auth + db instances.
|
|
3893
|
+
*
|
|
3894
|
+
* In managed mode (CALLBACK_TOKEN), the platform resolves the app from
|
|
3895
|
+
* the token — no appId needed. With an API key, appId is required.
|
|
3896
|
+
*/
|
|
3897
|
+
async _hydrateContext() {
|
|
3898
|
+
if (!this._appId && this._authType !== "internal") {
|
|
3899
|
+
throw new MindStudioError(
|
|
3900
|
+
"No app ID available for context resolution. Pass `appId` to the constructor, set the MINDSTUDIO_APP_ID environment variable, or make a step execution call first (which auto-detects the app ID).",
|
|
3901
|
+
"missing_app_id",
|
|
3902
|
+
400
|
|
3903
|
+
);
|
|
3904
|
+
}
|
|
3905
|
+
const context = await this.getAppContext(this._appId);
|
|
3906
|
+
this._applyContext(context);
|
|
3907
|
+
}
|
|
3908
|
+
/**
|
|
3909
|
+
* @internal Apply a resolved context object — creates AuthContext and Db.
|
|
3910
|
+
* Used by both the HTTP path and sandbox hydration.
|
|
3911
|
+
*/
|
|
3912
|
+
_applyContext(context) {
|
|
3913
|
+
this._context = context;
|
|
3914
|
+
this._auth = new AuthContext(context.auth);
|
|
3915
|
+
this._db = createDb(
|
|
3916
|
+
context.databases,
|
|
3917
|
+
this._executeDbBatch.bind(this),
|
|
3918
|
+
context.authConfig,
|
|
3919
|
+
this._syncRoles.bind(this)
|
|
3920
|
+
);
|
|
3921
|
+
}
|
|
3922
|
+
/**
|
|
3923
|
+
* @internal Try to hydrate context synchronously from sandbox globals.
|
|
3924
|
+
* Called in the constructor when CALLBACK_TOKEN auth is detected.
|
|
3925
|
+
*
|
|
3926
|
+
* The MindStudio sandbox pre-populates `globalThis.ai` with:
|
|
3927
|
+
* - `ai.auth`: { userId, roleAssignments[] }
|
|
3928
|
+
* - `ai.databases`: [{ id, name, tables[] }]
|
|
3929
|
+
*/
|
|
3930
|
+
_trySandboxHydration() {
|
|
3931
|
+
const ai = globalThis.ai;
|
|
3932
|
+
if (ai?.auth && ai?.databases) {
|
|
3933
|
+
this._applyContext({
|
|
3934
|
+
auth: ai.auth,
|
|
3935
|
+
databases: ai.databases,
|
|
3936
|
+
authConfig: ai.authConfig
|
|
3937
|
+
});
|
|
3938
|
+
}
|
|
3939
|
+
}
|
|
3940
|
+
/**
|
|
3941
|
+
* @internal Execute a batch of SQL queries against a managed database.
|
|
3942
|
+
* Used as the `executeBatch` callback for Table/Query instances.
|
|
3943
|
+
*
|
|
3944
|
+
* Calls `POST /_internal/v2/db/query` directly with the hook token
|
|
3945
|
+
* (raw, no Bearer prefix). All queries run on a single SQLite connection,
|
|
3946
|
+
* enabling RETURNING clauses and multi-statement batches.
|
|
3947
|
+
*/
|
|
3948
|
+
async _executeDbBatch(databaseId, queries) {
|
|
3949
|
+
const url = `${this._httpConfig.baseUrl}/_internal/v2/db/query`;
|
|
3950
|
+
const res = await fetch(url, {
|
|
3951
|
+
method: "POST",
|
|
3952
|
+
headers: {
|
|
3953
|
+
"Content-Type": "application/json",
|
|
3954
|
+
Authorization: this._token
|
|
3955
|
+
},
|
|
3956
|
+
body: JSON.stringify({ databaseId, queries })
|
|
3957
|
+
});
|
|
3958
|
+
if (!res.ok) {
|
|
3959
|
+
let message = `Database query failed: ${res.status} ${res.statusText}`;
|
|
3960
|
+
let code = "db_query_error";
|
|
3961
|
+
try {
|
|
3962
|
+
const text = await res.text();
|
|
3963
|
+
try {
|
|
3964
|
+
const body = JSON.parse(text);
|
|
3965
|
+
const errMsg = body.error ?? body.message ?? body.details;
|
|
3966
|
+
if (errMsg) message = errMsg;
|
|
3967
|
+
if (body.code) code = body.code;
|
|
3968
|
+
} catch {
|
|
3969
|
+
if (text && text.length < 500) message = text;
|
|
3970
|
+
}
|
|
3971
|
+
} catch {
|
|
3972
|
+
}
|
|
3973
|
+
throw new MindStudioError(
|
|
3974
|
+
`[db] ${message}`,
|
|
3975
|
+
code,
|
|
3976
|
+
res.status
|
|
3977
|
+
);
|
|
3978
|
+
}
|
|
3979
|
+
const data = await res.json();
|
|
3980
|
+
return data.results;
|
|
3981
|
+
}
|
|
3982
|
+
/**
|
|
3983
|
+
* @internal Sync a user's roles to the platform after a successful
|
|
3984
|
+
* auth table write. Calls POST /_internal/v2/auth/sync-user.
|
|
3985
|
+
* Fire-and-forget: errors are caught and logged, never propagated.
|
|
3986
|
+
*/
|
|
3987
|
+
async _syncRoles(userId, roles) {
|
|
3988
|
+
try {
|
|
3989
|
+
const url = `${this._httpConfig.baseUrl}/_internal/v2/auth/sync-user`;
|
|
3990
|
+
const res = await fetch(url, {
|
|
3991
|
+
method: "POST",
|
|
3992
|
+
headers: {
|
|
3993
|
+
"Content-Type": "application/json",
|
|
3994
|
+
Authorization: this._token
|
|
3995
|
+
},
|
|
3996
|
+
body: JSON.stringify({
|
|
3997
|
+
appId: this._appId,
|
|
3998
|
+
userId,
|
|
3999
|
+
roles
|
|
4000
|
+
})
|
|
4001
|
+
});
|
|
4002
|
+
if (!res.ok) {
|
|
4003
|
+
const text = await res.text().catch(() => "");
|
|
4004
|
+
console.warn(
|
|
4005
|
+
`[mindstudio] Failed to sync roles for user ${userId}: ${res.status} ${text}`
|
|
4006
|
+
);
|
|
4007
|
+
}
|
|
4008
|
+
} catch (err) {
|
|
4009
|
+
console.warn(
|
|
4010
|
+
`[mindstudio] Failed to sync roles for user ${userId}:`,
|
|
4011
|
+
err
|
|
4012
|
+
);
|
|
4013
|
+
}
|
|
4014
|
+
}
|
|
4015
|
+
/**
|
|
4016
|
+
* @internal Create a lazy Db proxy that auto-hydrates context.
|
|
4017
|
+
*
|
|
4018
|
+
* defineTable() returns Table instances immediately (no async needed).
|
|
4019
|
+
* But the Table's executeBatch callback is wrapped to call ensureContext()
|
|
4020
|
+
* before the first query, so context is fetched lazily.
|
|
4021
|
+
*/
|
|
4022
|
+
_createLazyDb() {
|
|
4023
|
+
const agent = this;
|
|
4024
|
+
return {
|
|
4025
|
+
defineTable(name, options) {
|
|
4026
|
+
const databaseHint = options?.database;
|
|
4027
|
+
const tableConfig = {
|
|
4028
|
+
databaseId: "",
|
|
4029
|
+
tableName: name,
|
|
4030
|
+
columns: [],
|
|
4031
|
+
unique: options?.unique,
|
|
4032
|
+
defaults: options?.defaults,
|
|
4033
|
+
executeBatch: async (queries) => {
|
|
4034
|
+
await agent.ensureContext();
|
|
4035
|
+
const ac = agent._context.authConfig;
|
|
4036
|
+
if (ac && ac.table === name && !tableConfig.managedColumns) {
|
|
4037
|
+
tableConfig.managedColumns = ac.columns;
|
|
4038
|
+
if (ac.columns.roles) {
|
|
4039
|
+
tableConfig.syncRoles = agent._syncRoles.bind(agent);
|
|
4040
|
+
}
|
|
4041
|
+
}
|
|
4042
|
+
const databases = agent._context.databases;
|
|
4043
|
+
let targetDb;
|
|
4044
|
+
if (databaseHint) {
|
|
4045
|
+
targetDb = databases.find(
|
|
4046
|
+
(d) => d.id === databaseHint || d.name === databaseHint
|
|
4047
|
+
);
|
|
4048
|
+
} else {
|
|
4049
|
+
targetDb = databases.find(
|
|
4050
|
+
(d) => d.tables.some((t) => t.name === name)
|
|
4051
|
+
);
|
|
4052
|
+
}
|
|
4053
|
+
const databaseId = targetDb?.id ?? databases[0]?.id ?? "";
|
|
4054
|
+
return agent._executeDbBatch(databaseId, queries);
|
|
4055
|
+
}
|
|
4056
|
+
};
|
|
4057
|
+
return new Table(tableConfig);
|
|
4058
|
+
},
|
|
4059
|
+
// Time helpers work without context
|
|
4060
|
+
now: () => Date.now(),
|
|
4061
|
+
days: (n) => n * 864e5,
|
|
4062
|
+
hours: (n) => n * 36e5,
|
|
4063
|
+
minutes: (n) => n * 6e4,
|
|
4064
|
+
ago: (ms) => Date.now() - ms,
|
|
4065
|
+
fromNow: (ms) => Date.now() + ms,
|
|
4066
|
+
// Batch needs context — hydrate first, then delegate to real db
|
|
4067
|
+
batch: ((...queries) => {
|
|
4068
|
+
return (async () => {
|
|
4069
|
+
await agent.ensureContext();
|
|
4070
|
+
return agent._db.batch(...queries);
|
|
4071
|
+
})();
|
|
4072
|
+
})
|
|
4073
|
+
};
|
|
4074
|
+
}
|
|
4075
|
+
// -------------------------------------------------------------------------
|
|
4076
|
+
// Helper methods — user resolution
|
|
4077
|
+
// -------------------------------------------------------------------------
|
|
4078
|
+
/**
|
|
4079
|
+
* Resolve a single user ID to display info (name, email, profile picture).
|
|
4080
|
+
*
|
|
4081
|
+
* Use this when you have a `User`-typed field value and need the person's
|
|
4082
|
+
* display name, email, or avatar. Returns null if the user ID is not found.
|
|
4083
|
+
*
|
|
4084
|
+
* Also available as a top-level import:
|
|
4085
|
+
* ```ts
|
|
4086
|
+
* import { resolveUser } from '@mindstudio-ai/agent';
|
|
4087
|
+
* ```
|
|
4088
|
+
*
|
|
4089
|
+
* @param userId - The user ID to resolve (a `User` branded string or plain UUID)
|
|
4090
|
+
* @returns Resolved user info, or null if not found
|
|
4091
|
+
*
|
|
4092
|
+
* @example
|
|
4093
|
+
* ```ts
|
|
4094
|
+
* const user = await agent.resolveUser(order.requestedBy);
|
|
4095
|
+
* if (user) {
|
|
4096
|
+
* console.log(user.name); // "Jane Smith"
|
|
4097
|
+
* console.log(user.email); // "jane@example.com"
|
|
4098
|
+
* console.log(user.profilePictureUrl); // "https://..." or null
|
|
4099
|
+
* }
|
|
4100
|
+
* ```
|
|
4101
|
+
*/
|
|
4102
|
+
async resolveUser(userId) {
|
|
4103
|
+
const { users } = await this.resolveUsers([userId]);
|
|
4104
|
+
return users[0] ?? null;
|
|
4105
|
+
}
|
|
4106
|
+
/**
|
|
4107
|
+
* Resolve multiple user IDs to display info in a single request.
|
|
4108
|
+
* Maximum 100 user IDs per request.
|
|
4109
|
+
*
|
|
4110
|
+
* Use this for batch resolution when you have multiple user references
|
|
4111
|
+
* to display (e.g. all approvers on a purchase order, all team members).
|
|
4112
|
+
*
|
|
4113
|
+
* @param userIds - Array of user IDs to resolve (max 100)
|
|
4114
|
+
* @returns Object with `users` array of resolved user info
|
|
4115
|
+
*
|
|
4116
|
+
* @example
|
|
4117
|
+
* ```ts
|
|
4118
|
+
* // Resolve all approvers at once
|
|
4119
|
+
* const approverIds = approvals.map(a => a.assignedTo);
|
|
4120
|
+
* const { users } = await agent.resolveUsers(approverIds);
|
|
4121
|
+
*
|
|
4122
|
+
* for (const u of users) {
|
|
4123
|
+
* console.log(`${u.name} (${u.email})`);
|
|
4124
|
+
* }
|
|
4125
|
+
* ```
|
|
4126
|
+
*/
|
|
4127
|
+
async resolveUsers(userIds) {
|
|
4128
|
+
const { data } = await request(
|
|
4129
|
+
this._httpConfig,
|
|
4130
|
+
"POST",
|
|
4131
|
+
"/helpers/resolve-users",
|
|
4132
|
+
{ userIds }
|
|
4133
|
+
);
|
|
4134
|
+
return data;
|
|
4135
|
+
}
|
|
4136
|
+
// -------------------------------------------------------------------------
|
|
4137
|
+
// App context
|
|
4138
|
+
// -------------------------------------------------------------------------
|
|
4139
|
+
/**
|
|
4140
|
+
* Get auth and database context for an app.
|
|
4141
|
+
*
|
|
4142
|
+
* Returns role assignments and managed database schemas. Useful for
|
|
4143
|
+
* hydrating `auth` and `db` namespaces when running outside the sandbox.
|
|
4144
|
+
*
|
|
4145
|
+
* When called with a CALLBACK_TOKEN (managed mode), `appId` is optional —
|
|
4146
|
+
* the platform resolves the app from the token. With an API key, `appId`
|
|
4147
|
+
* is required.
|
|
4148
|
+
*
|
|
4149
|
+
* ```ts
|
|
4150
|
+
* const ctx = await agent.getAppContext('your-app-id');
|
|
4151
|
+
* console.log(ctx.auth.roleAssignments, ctx.databases);
|
|
4152
|
+
* ```
|
|
4153
|
+
*/
|
|
4154
|
+
async getAppContext(appId) {
|
|
4155
|
+
const query = appId ? `?appId=${encodeURIComponent(appId)}` : "";
|
|
4156
|
+
const { data } = await request(
|
|
4157
|
+
this._httpConfig,
|
|
4158
|
+
"GET",
|
|
4159
|
+
`/helpers/app-context${query}`
|
|
4160
|
+
);
|
|
4161
|
+
return data;
|
|
4162
|
+
}
|
|
4163
|
+
// -------------------------------------------------------------------------
|
|
4164
|
+
// Account methods
|
|
4165
|
+
// -------------------------------------------------------------------------
|
|
4166
|
+
/** Update the display name of the authenticated user/agent. */
|
|
4167
|
+
async changeName(displayName) {
|
|
4168
|
+
await request(this._httpConfig, "POST", "/account/change-name", {
|
|
4169
|
+
name: displayName
|
|
4170
|
+
});
|
|
4171
|
+
}
|
|
4172
|
+
/** Update the profile picture of the authenticated user/agent. */
|
|
4173
|
+
async changeProfilePicture(url) {
|
|
4174
|
+
await request(this._httpConfig, "POST", "/account/change-profile-picture", {
|
|
4175
|
+
url
|
|
4176
|
+
});
|
|
4177
|
+
}
|
|
4178
|
+
/**
|
|
4179
|
+
* Upload a file to the MindStudio CDN.
|
|
4180
|
+
*
|
|
4181
|
+
* Gets a signed upload URL, PUTs the file content, and returns the
|
|
4182
|
+
* permanent public URL.
|
|
4183
|
+
*/
|
|
4184
|
+
async uploadFile(content, options) {
|
|
4185
|
+
const { data } = await request(
|
|
4186
|
+
this._httpConfig,
|
|
4187
|
+
"POST",
|
|
4188
|
+
"/account/upload",
|
|
4189
|
+
{
|
|
4190
|
+
extension: options.extension,
|
|
4191
|
+
...options.type != null && { type: options.type }
|
|
4192
|
+
}
|
|
4193
|
+
);
|
|
4194
|
+
const buf = content.buffer.slice(
|
|
4195
|
+
content.byteOffset,
|
|
4196
|
+
content.byteOffset + content.byteLength
|
|
4197
|
+
);
|
|
4198
|
+
const res = await fetch(data.uploadUrl, {
|
|
4199
|
+
method: "PUT",
|
|
4200
|
+
body: buf,
|
|
4201
|
+
headers: options.type ? { "Content-Type": options.type } : {}
|
|
4202
|
+
});
|
|
4203
|
+
if (!res.ok) {
|
|
4204
|
+
const errorBody = await res.json().catch(() => ({}));
|
|
4205
|
+
throw new MindStudioError(
|
|
4206
|
+
errorBody.message ?? errorBody.error ?? `Upload failed: ${res.status} ${res.statusText}`,
|
|
4207
|
+
errorBody.code ?? "upload_error",
|
|
4208
|
+
res.status,
|
|
4209
|
+
errorBody
|
|
4210
|
+
);
|
|
4211
|
+
}
|
|
4212
|
+
return { url: data.url };
|
|
4213
|
+
}
|
|
4214
|
+
};
|
|
4215
|
+
function sleep2(ms) {
|
|
4216
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
4217
|
+
}
|
|
4218
|
+
applyStepMethods(MindStudioAgent);
|
|
4219
|
+
function resolveStepType(name) {
|
|
4220
|
+
const meta = stepMetadata[name];
|
|
4221
|
+
return meta ? meta.stepType : name;
|
|
4222
|
+
}
|
|
4223
|
+
function resolveToken(provided, config) {
|
|
4224
|
+
if (process.env.CALLBACK_TOKEN)
|
|
4225
|
+
return { token: process.env.CALLBACK_TOKEN, authType: "internal" };
|
|
4226
|
+
if (provided) return { token: provided, authType: "apiKey" };
|
|
4227
|
+
if (process.env.MINDSTUDIO_API_KEY)
|
|
4228
|
+
return { token: process.env.MINDSTUDIO_API_KEY, authType: "apiKey" };
|
|
4229
|
+
if (config?.apiKey)
|
|
4230
|
+
return { token: config.apiKey, authType: "apiKey" };
|
|
4231
|
+
throw new MindStudioError(
|
|
4232
|
+
"No API key provided. Run `mindstudio login`, pass `apiKey` to the constructor, or set the MINDSTUDIO_API_KEY environment variable.",
|
|
4233
|
+
"missing_api_key",
|
|
4234
|
+
401
|
|
4235
|
+
);
|
|
4236
|
+
}
|
|
4237
|
+
|
|
4238
|
+
// src/generated/snippets.ts
|
|
4239
|
+
var monacoSnippets = {
|
|
4240
|
+
"activeCampaignAddNote": { fields: [["contactId", "string"], ["note", "string"]], outputKeys: [] },
|
|
4241
|
+
"activeCampaignCreateContact": { fields: [["email", "string"], ["firstName", "string"], ["lastName", "string"], ["phone", "string"], ["accountId", "string"], ["customFields", "object"]], outputKeys: ["contactId"] },
|
|
4242
|
+
"addSubtitlesToVideo": { fields: [["videoUrl", "string"], ["language", "string"], ["fontName", "string"], ["fontSize", "number"], ["fontWeight", ["normal", "bold", "black"]], ["fontColor", ["white", "black", "red", "green", "blue", "yellow", "orange", "purple", "pink", "brown", "gray", "cyan", "magenta"]], ["highlightColor", ["white", "black", "red", "green", "blue", "yellow", "orange", "purple", "pink", "brown", "gray", "cyan", "magenta"]], ["strokeWidth", "number"], ["strokeColor", ["black", "white", "red", "green", "blue", "yellow", "orange", "purple", "pink", "brown", "gray", "cyan", "magenta"]], ["backgroundColor", ["black", "white", "red", "green", "blue", "yellow", "orange", "purple", "pink", "brown", "gray", "cyan", "magenta", "none"]], ["backgroundOpacity", "number"], ["position", ["top", "center", "bottom"]], ["yOffset", "number"], ["wordsPerSubtitle", "number"], ["enableAnimation", "boolean"]], outputKeys: ["videoUrl"] },
|
|
4243
|
+
"airtableCreateUpdateRecord": { fields: [["baseId", "string"], ["tableId", "string"], ["fields", "string"], ["recordData", "object"]], outputKeys: ["recordId"] },
|
|
4244
|
+
"airtableDeleteRecord": { fields: [["baseId", "string"], ["tableId", "string"], ["recordId", "string"]], outputKeys: ["deleted"] },
|
|
4245
|
+
"airtableGetRecord": { fields: [["baseId", "string"], ["tableId", "string"], ["recordId", "string"]], outputKeys: ["record"] },
|
|
4246
|
+
"airtableGetTableRecords": { fields: [["baseId", "string"], ["tableId", "string"]], outputKeys: ["records"] },
|
|
4247
|
+
"analyzeImage": { fields: [["prompt", "string"], ["imageUrl", "string"]], outputKeys: ["analysis"] },
|
|
4248
|
+
"analyzeVideo": { fields: [["prompt", "string"], ["videoUrl", "string"]], outputKeys: ["analysis"] },
|
|
4249
|
+
"captureThumbnail": { fields: [["videoUrl", "string"], ["at", "string"]], outputKeys: ["thumbnailUrl"] },
|
|
4250
|
+
"checkAppRole": { fields: [["roleName", "string"]], outputKeys: ["hasRole", "userRoles"] },
|
|
4251
|
+
"codaCreateUpdatePage": { fields: [["pageData", "object"]], outputKeys: ["pageId"] },
|
|
4252
|
+
"codaCreateUpdateRow": { fields: [["docId", "string"], ["tableId", "string"], ["rowData", "object"]], outputKeys: ["rowId"] },
|
|
4253
|
+
"codaFindRow": { fields: [["docId", "string"], ["tableId", "string"], ["rowData", "object"]], outputKeys: ["row"] },
|
|
4254
|
+
"codaGetPage": { fields: [["docId", "string"], ["pageId", "string"]], outputKeys: ["content"] },
|
|
4255
|
+
"codaGetTableRows": { fields: [["docId", "string"], ["tableId", "string"]], outputKeys: ["rows"] },
|
|
4256
|
+
"convertPdfToImages": { fields: [["pdfUrl", "string"]], outputKeys: ["imageUrls"] },
|
|
4257
|
+
"createDataSource": { fields: [["name", "string"]], outputKeys: [] },
|
|
4258
|
+
"createGmailDraft": { fields: [["to", "string"], ["subject", "string"], ["message", "string"], ["messageType", ["plain", "html", "markdown"]]], outputKeys: ["draftId"] },
|
|
4259
|
+
"createGoogleCalendarEvent": { fields: [["summary", "string"], ["startDateTime", "string"], ["endDateTime", "string"]], outputKeys: ["eventId", "htmlLink"] },
|
|
4260
|
+
"createGoogleDoc": { fields: [["title", "string"], ["text", "string"], ["textType", ["plain", "html", "markdown"]]], outputKeys: ["documentUrl"] },
|
|
4261
|
+
"createGoogleSheet": { fields: [["title", "string"], ["text", "string"]], outputKeys: ["spreadsheetUrl"] },
|
|
4262
|
+
"deleteDataSource": { fields: [["dataSourceId", "string"]], outputKeys: [] },
|
|
4263
|
+
"deleteDataSourceDocument": { fields: [["dataSourceId", "string"], ["documentId", "string"]], outputKeys: [] },
|
|
4264
|
+
"deleteGmailEmail": { fields: [["messageId", "string"]], outputKeys: [] },
|
|
4265
|
+
"deleteGoogleCalendarEvent": { fields: [["eventId", "string"]], outputKeys: [] },
|
|
4266
|
+
"deleteGoogleSheetRows": { fields: [["documentId", "string"], ["startRow", "string"], ["endRow", "string"]], outputKeys: [] },
|
|
4267
|
+
"detectChanges": { fields: [["mode", ["ai", "comparison"]], ["input", "string"]], outputKeys: ["hasChanged", "currentValue", "previousValue", "isFirstRun"] },
|
|
4268
|
+
"detectPII": { fields: [["input", "string"], ["language", "string"], ["entities", "array"]], outputKeys: ["detected", "detections"] },
|
|
4269
|
+
"discordEditMessage": { fields: [["botToken", "string"], ["channelId", "string"], ["messageId", "string"], ["text", "string"]], outputKeys: [] },
|
|
4270
|
+
"discordSendFollowUp": { fields: [["applicationId", "string"], ["interactionToken", "string"], ["text", "string"]], outputKeys: ["messageId"] },
|
|
4271
|
+
"discordSendMessage": { fields: [["mode", ["edit", "send"]], ["text", "string"]], outputKeys: [] },
|
|
4272
|
+
"downloadVideo": { fields: [["videoUrl", "string"], ["format", ["mp4", "mp3"]]], outputKeys: ["videoUrl"] },
|
|
4273
|
+
"enhanceImageGenerationPrompt": { fields: [["initialPrompt", "string"], ["includeNegativePrompt", "boolean"], ["systemPrompt", "string"]], outputKeys: ["prompt"] },
|
|
4274
|
+
"enhanceVideoGenerationPrompt": { fields: [["initialPrompt", "string"], ["includeNegativePrompt", "boolean"], ["systemPrompt", "string"]], outputKeys: ["prompt"] },
|
|
4275
|
+
"enrichPerson": { fields: [["params", "object"]], outputKeys: ["data"] },
|
|
4276
|
+
"extractAudioFromVideo": { fields: [["videoUrl", "string"]], outputKeys: ["audioUrl"] },
|
|
4277
|
+
"extractText": { fields: [["url", "string"]], outputKeys: ["text"] },
|
|
4278
|
+
"fetchDataSourceDocument": { fields: [["dataSourceId", "string"], ["documentId", "string"]], outputKeys: [] },
|
|
4279
|
+
"fetchGoogleDoc": { fields: [["documentId", "string"], ["exportType", ["html", "markdown", "json", "plain"]]], outputKeys: ["content"] },
|
|
4280
|
+
"fetchGoogleSheet": { fields: [["spreadsheetId", "string"], ["range", "string"], ["exportType", ["csv", "json"]]], outputKeys: ["content"] },
|
|
4281
|
+
"fetchSlackChannelHistory": { fields: [["channelId", "string"]], outputKeys: ["messages"] },
|
|
4282
|
+
"fetchYoutubeCaptions": { fields: [["videoUrl", "string"], ["exportType", ["text", "json"]], ["language", "string"]], outputKeys: ["transcripts"] },
|
|
4283
|
+
"fetchYoutubeChannel": { fields: [["channelUrl", "string"]], outputKeys: [] },
|
|
4284
|
+
"fetchYoutubeComments": { fields: [["videoUrl", "string"], ["exportType", ["text", "json"]], ["limitPages", "string"]], outputKeys: ["comments"] },
|
|
4285
|
+
"fetchYoutubeVideo": { fields: [["videoUrl", "string"]], outputKeys: [] },
|
|
4286
|
+
"generateAsset": { fields: [["source", "string"], ["sourceType", ["html", "markdown", "spa", "raw", "dynamic", "customInterface"]], ["outputFormat", ["pdf", "png", "html", "mp4", "openGraph"]], ["pageSize", ["full", "letter", "A4", "custom"]], ["testData", "object"]], outputKeys: ["url"] },
|
|
4287
|
+
"generateChart": { fields: [["chart", "object"]], outputKeys: ["chartUrl"] },
|
|
4288
|
+
"generateImage": { fields: [["prompt", "string"]], outputKeys: ["imageUrl"] },
|
|
4289
|
+
"generateLipsync": { fields: [], outputKeys: [] },
|
|
4290
|
+
"generateMusic": { fields: [["text", "string"]], outputKeys: [] },
|
|
4291
|
+
"generatePdf": { fields: [["source", "string"], ["sourceType", ["html", "markdown", "spa", "raw", "dynamic", "customInterface"]], ["outputFormat", ["pdf", "png", "html", "mp4", "openGraph"]], ["pageSize", ["full", "letter", "A4", "custom"]], ["testData", "object"]], outputKeys: ["url"] },
|
|
4292
|
+
"generateStaticVideoFromImage": { fields: [["imageUrl", "string"], ["duration", "string"]], outputKeys: ["videoUrl"] },
|
|
4293
|
+
"generateText": { fields: [["message", "string"]], outputKeys: ["content"] },
|
|
4294
|
+
"generateVideo": { fields: [["prompt", "string"]], outputKeys: ["videoUrl"] },
|
|
4295
|
+
"getGmailAttachments": { fields: [["messageId", "string"]], outputKeys: [] },
|
|
4296
|
+
"getGmailDraft": { fields: [["draftId", "string"]], outputKeys: ["draftId", "messageId", "subject", "to", "from", "body"] },
|
|
4297
|
+
"getGmailEmail": { fields: [["messageId", "string"]], outputKeys: ["messageId", "subject", "from", "to", "date", "body", "labels"] },
|
|
4298
|
+
"getGmailUnreadCount": { fields: [], outputKeys: [] },
|
|
4299
|
+
"getGoogleCalendarEvent": { fields: [["eventId", "string"], ["exportType", ["json", "text"]]], outputKeys: ["event"] },
|
|
4300
|
+
"getGoogleDriveFile": { fields: [["fileId", "string"]], outputKeys: ["url", "name", "mimeType", "size"] },
|
|
4301
|
+
"getGoogleSheetInfo": { fields: [["documentId", "string"]], outputKeys: ["title", "sheets"] },
|
|
4302
|
+
"getMediaMetadata": { fields: [["mediaUrl", "string"]], outputKeys: ["metadata"] },
|
|
4303
|
+
"httpRequest": { fields: [["url", "string"], ["method", "string"], ["headers", "object"], ["queryParams", "object"], ["body", "string"], ["bodyItems", "object"], ["contentType", ["none", "application/json", "application/x-www-form-urlencoded", "multipart/form-data", "custom"]], ["customContentType", "string"]], outputKeys: ["ok", "status", "statusText", "response"] },
|
|
4304
|
+
"hubspotCreateCompany": { fields: [["company", "object"], ["enabledProperties", "array"]], outputKeys: ["companyId"] },
|
|
4305
|
+
"hubspotCreateContact": { fields: [["contact", "object"], ["enabledProperties", "array"], ["companyDomain", "string"]], outputKeys: ["contactId"] },
|
|
4306
|
+
"hubspotGetCompany": { fields: [["searchBy", ["domain", "id"]], ["companyDomain", "string"], ["companyId", "string"], ["additionalProperties", "array"]], outputKeys: ["company"] },
|
|
4307
|
+
"hubspotGetContact": { fields: [["searchBy", ["email", "id"]], ["contactEmail", "string"], ["contactId", "string"], ["additionalProperties", "array"]], outputKeys: ["contact"] },
|
|
4308
|
+
"hunterApiCompanyEnrichment": { fields: [["domain", "string"]], outputKeys: ["data"] },
|
|
4309
|
+
"hunterApiDomainSearch": { fields: [["domain", "string"]], outputKeys: ["data"] },
|
|
4310
|
+
"hunterApiEmailFinder": { fields: [["domain", "string"], ["firstName", "string"], ["lastName", "string"]], outputKeys: ["data"] },
|
|
4311
|
+
"hunterApiEmailVerification": { fields: [["email", "string"]], outputKeys: ["data"] },
|
|
4312
|
+
"hunterApiPersonEnrichment": { fields: [["email", "string"]], outputKeys: ["data"] },
|
|
4313
|
+
"imageFaceSwap": { fields: [["imageUrl", "string"], ["faceImageUrl", "string"], ["engine", "string"]], outputKeys: ["imageUrl"] },
|
|
4314
|
+
"imageRemoveWatermark": { fields: [["imageUrl", "string"], ["engine", "string"]], outputKeys: ["imageUrl"] },
|
|
4315
|
+
"insertVideoClips": { fields: [["baseVideoUrl", "string"], ["overlayVideos", "array"]], outputKeys: ["videoUrl"] },
|
|
4316
|
+
"listDataSources": { fields: [], outputKeys: [] },
|
|
4317
|
+
"listGmailDrafts": { fields: [["exportType", ["json", "text"]]], outputKeys: ["drafts"] },
|
|
4318
|
+
"listGmailLabels": { fields: [], outputKeys: [] },
|
|
4319
|
+
"listGoogleCalendarEvents": { fields: [["limit", "number"], ["exportType", ["json", "text"]]], outputKeys: ["events"] },
|
|
4320
|
+
"listGoogleDriveFiles": { fields: [["exportType", ["json", "text"]]], outputKeys: ["files"] },
|
|
4321
|
+
"listRecentGmailEmails": { fields: [["exportType", ["json", "text"]], ["limit", "string"]], outputKeys: [] },
|
|
4322
|
+
"logic": { fields: [["context", "string"], ["cases", "array"]], outputKeys: ["selectedCase"] },
|
|
4323
|
+
"makeDotComRunScenario": { fields: [["webhookUrl", "string"], ["input", "object"]], outputKeys: ["data"] },
|
|
4324
|
+
"mergeAudio": { fields: [["mp3Urls", "array"]], outputKeys: ["audioUrl"] },
|
|
4325
|
+
"mergeVideos": { fields: [["videoUrls", "array"]], outputKeys: ["videoUrl"] },
|
|
4326
|
+
"mixAudioIntoVideo": { fields: [["videoUrl", "string"], ["audioUrl", "string"], ["options", "object"]], outputKeys: ["videoUrl"] },
|
|
4327
|
+
"muteVideo": { fields: [["videoUrl", "string"]], outputKeys: ["videoUrl"] },
|
|
4328
|
+
"n8nRunNode": { fields: [["method", "string"], ["authentication", ["none", "basic", "string"]], ["user", "string"], ["password", "string"], ["webhookUrl", "string"], ["input", "object"]], outputKeys: ["data"] },
|
|
4329
|
+
"notionCreatePage": { fields: [["pageId", "string"], ["content", "string"], ["title", "string"]], outputKeys: ["pageId", "pageUrl"] },
|
|
4330
|
+
"notionUpdatePage": { fields: [["pageId", "string"], ["content", "string"], ["mode", ["append", "overwrite"]]], outputKeys: ["pageId", "pageUrl"] },
|
|
4331
|
+
"peopleSearch": { fields: [["smartQuery", "string"], ["enrichPeople", "boolean"], ["enrichOrganizations", "boolean"], ["limit", "string"], ["page", "string"], ["params", "object"]], outputKeys: ["results"] },
|
|
4332
|
+
"postToLinkedIn": { fields: [["message", "string"], ["visibility", ["PUBLIC", "CONNECTIONS"]]], outputKeys: [] },
|
|
4333
|
+
"postToSlackChannel": { fields: [["channelId", "string"], ["messageType", ["string", "blocks"]], ["message", "string"]], outputKeys: [] },
|
|
4334
|
+
"postToX": { fields: [["text", "string"]], outputKeys: [] },
|
|
4335
|
+
"postToZapier": { fields: [["webhookUrl", "string"], ["input", "object"]], outputKeys: ["data"] },
|
|
4336
|
+
"queryAppDatabase": { fields: [["databaseId", "string"], ["sql", "string"]], outputKeys: ["rows", "changes"] },
|
|
4337
|
+
"queryDataSource": { fields: [["dataSourceId", "string"], ["query", "string"], ["maxResults", "number"]], outputKeys: ["text", "chunks", "query", "citations", "latencyMs"] },
|
|
4338
|
+
"queryExternalDatabase": { fields: [["query", "string"], ["outputFormat", ["json", "csv"]]], outputKeys: ["data"] },
|
|
4339
|
+
"redactPII": { fields: [["input", "string"], ["language", "string"], ["entities", "array"]], outputKeys: ["text"] },
|
|
4340
|
+
"removeBackgroundFromImage": { fields: [["imageUrl", "string"]], outputKeys: ["imageUrl"] },
|
|
4341
|
+
"replyToGmailEmail": { fields: [["messageId", "string"], ["message", "string"], ["messageType", ["plain", "html", "markdown"]]], outputKeys: ["messageId"] },
|
|
4342
|
+
"resizeVideo": { fields: [["videoUrl", "string"], ["mode", ["fit", "exact"]]], outputKeys: ["videoUrl"] },
|
|
4343
|
+
"runFromConnectorRegistry": { fields: [["actionId", "string"], ["displayName", "string"], ["icon", "string"], ["configurationValues", "object"]], outputKeys: ["data"] },
|
|
4344
|
+
"runPackagedWorkflow": { fields: [["appId", "string"], ["workflowId", "string"], ["inputVariables", "object"], ["outputVariables", "object"], ["name", "string"]], outputKeys: ["data"] },
|
|
4345
|
+
"scrapeFacebookPage": { fields: [["pageUrl", "string"]], outputKeys: ["data"] },
|
|
4346
|
+
"scrapeFacebookPosts": { fields: [["pageUrl", "string"]], outputKeys: ["data"] },
|
|
4347
|
+
"scrapeInstagramComments": { fields: [["postUrl", "string"], ["resultsLimit", "string"]], outputKeys: ["data"] },
|
|
4348
|
+
"scrapeInstagramMentions": { fields: [["profileUrl", "string"], ["resultsLimit", "string"]], outputKeys: ["data"] },
|
|
4349
|
+
"scrapeInstagramPosts": { fields: [["profileUrl", "string"], ["resultsLimit", "string"], ["onlyPostsNewerThan", "string"]], outputKeys: ["data"] },
|
|
4350
|
+
"scrapeInstagramProfile": { fields: [["profileUrl", "string"]], outputKeys: ["data"] },
|
|
4351
|
+
"scrapeInstagramReels": { fields: [["profileUrl", "string"], ["resultsLimit", "string"]], outputKeys: ["data"] },
|
|
4352
|
+
"scrapeLinkedInCompany": { fields: [["url", "string"]], outputKeys: ["company"] },
|
|
4353
|
+
"scrapeLinkedInProfile": { fields: [["url", "string"]], outputKeys: ["profile"] },
|
|
4354
|
+
"scrapeMetaThreadsProfile": { fields: [["profileUrl", "string"]], outputKeys: ["data"] },
|
|
4355
|
+
"scrapeUrl": { fields: [["url", "string"]], outputKeys: ["content"] },
|
|
4356
|
+
"scrapeXPost": { fields: [["url", "string"]], outputKeys: ["post"] },
|
|
4357
|
+
"scrapeXProfile": { fields: [["url", "string"]], outputKeys: ["profile"] },
|
|
4358
|
+
"screenshotUrl": { fields: [["url", "string"]], outputKeys: ["screenshotUrl"] },
|
|
4359
|
+
"searchGmailEmails": { fields: [["query", "string"], ["exportType", ["json", "text"]], ["limit", "string"]], outputKeys: ["emails"] },
|
|
4360
|
+
"searchGoogle": { fields: [["query", "string"], ["exportType", ["text", "json"]]], outputKeys: ["results"] },
|
|
4361
|
+
"searchGoogleCalendarEvents": { fields: [["exportType", ["json", "text"]]], outputKeys: ["events"] },
|
|
4362
|
+
"searchGoogleDrive": { fields: [["query", "string"], ["exportType", ["json", "text"]]], outputKeys: ["files"] },
|
|
4363
|
+
"searchGoogleImages": { fields: [["query", "string"], ["exportType", ["text", "json"]]], outputKeys: ["images"] },
|
|
4364
|
+
"searchGoogleNews": { fields: [["text", "string"], ["exportType", ["text", "json"]]], outputKeys: ["articles"] },
|
|
4365
|
+
"searchGoogleTrends": { fields: [["text", "string"], ["hl", "string"], ["geo", "string"], ["data_type", ["TIMESERIES", "GEO_MAP", "GEO_MAP_0", "RELATED_TOPICS", "RELATED_QUERIES"]], ["cat", "string"], ["date", "string"], ["ts", "string"]], outputKeys: ["trends"] },
|
|
4366
|
+
"searchPerplexity": { fields: [["query", "string"], ["exportType", ["text", "json"]]], outputKeys: ["results"] },
|
|
4367
|
+
"searchXPosts": { fields: [["query", "string"], ["scope", ["recent", "all"]], ["options", "object"]], outputKeys: ["posts"] },
|
|
4368
|
+
"searchYoutube": { fields: [["query", "string"], ["limitPages", "string"], ["filter", "string"], ["filterType", "string"]], outputKeys: ["results"] },
|
|
4369
|
+
"searchYoutubeTrends": { fields: [["bp", ["now", "music", "gaming", "films"]], ["hl", "string"], ["gl", "string"]], outputKeys: [] },
|
|
4370
|
+
"sendEmail": { fields: [["subject", "string"], ["body", "string"]], outputKeys: ["recipients"] },
|
|
4371
|
+
"sendGmailDraft": { fields: [["draftId", "string"]], outputKeys: [] },
|
|
4372
|
+
"sendGmailMessage": { fields: [["to", "string"], ["subject", "string"], ["message", "string"], ["messageType", ["plain", "html", "markdown"]]], outputKeys: ["messageId"] },
|
|
4373
|
+
"sendSMS": { fields: [["body", "string"]], outputKeys: [] },
|
|
4374
|
+
"setGmailReadStatus": { fields: [["messageIds", "string"], ["markAsRead", "boolean"]], outputKeys: [] },
|
|
4375
|
+
"setRunTitle": { fields: [["title", "string"]], outputKeys: [] },
|
|
4376
|
+
"setVariable": { fields: [["value", "string"]], outputKeys: [] },
|
|
4377
|
+
"telegramEditMessage": { fields: [["botToken", "string"], ["chatId", "string"], ["messageId", "string"], ["text", "string"]], outputKeys: [] },
|
|
4378
|
+
"telegramReplyToMessage": { fields: [["botToken", "string"], ["chatId", "string"], ["replyToMessageId", "string"], ["text", "string"]], outputKeys: ["messageId"] },
|
|
4379
|
+
"telegramSendAudio": { fields: [["botToken", "string"], ["chatId", "string"], ["audioUrl", "string"], ["mode", ["audio", "voice"]]], outputKeys: [] },
|
|
4380
|
+
"telegramSendFile": { fields: [["botToken", "string"], ["chatId", "string"], ["fileUrl", "string"]], outputKeys: [] },
|
|
4381
|
+
"telegramSendImage": { fields: [["botToken", "string"], ["chatId", "string"], ["imageUrl", "string"]], outputKeys: [] },
|
|
4382
|
+
"telegramSendMessage": { fields: [["botToken", "string"], ["chatId", "string"], ["text", "string"]], outputKeys: ["messageId"] },
|
|
4383
|
+
"telegramSendVideo": { fields: [["botToken", "string"], ["chatId", "string"], ["videoUrl", "string"]], outputKeys: [] },
|
|
4384
|
+
"telegramSetTyping": { fields: [["botToken", "string"], ["chatId", "string"]], outputKeys: [] },
|
|
4385
|
+
"textToSpeech": { fields: [["text", "string"]], outputKeys: ["audioUrl"] },
|
|
4386
|
+
"transcribeAudio": { fields: [["audioUrl", "string"], ["prompt", "string"]], outputKeys: ["text"] },
|
|
4387
|
+
"trimMedia": { fields: [["inputUrl", "string"]], outputKeys: ["mediaUrl"] },
|
|
4388
|
+
"updateGmailLabels": { fields: [["query", "string"], ["messageIds", "string"], ["addLabelIds", "string"], ["removeLabelIds", "string"]], outputKeys: ["updatedMessageIds"] },
|
|
4389
|
+
"updateGoogleCalendarEvent": { fields: [["eventId", "string"]], outputKeys: ["eventId", "htmlLink"] },
|
|
4390
|
+
"updateGoogleDoc": { fields: [["documentId", "string"], ["text", "string"], ["textType", ["plain", "html", "markdown"]], ["operationType", ["addToTop", "addToBottom", "overwrite"]]], outputKeys: ["documentUrl"] },
|
|
4391
|
+
"updateGoogleSheet": { fields: [["text", "string"], ["spreadsheetId", "string"], ["range", "string"], ["operationType", ["addToBottom", "overwrite", "range"]]], outputKeys: ["spreadsheetUrl"] },
|
|
4392
|
+
"uploadDataSourceDocument": { fields: [["dataSourceId", "string"], ["file", "string"], ["fileName", "string"]], outputKeys: [] },
|
|
4393
|
+
"upscaleImage": { fields: [["imageUrl", "string"], ["targetResolution", ["2k", "4k", "8k"]], ["engine", ["standard", "pro"]]], outputKeys: ["imageUrl"] },
|
|
4394
|
+
"upscaleVideo": { fields: [["videoUrl", "string"], ["targetResolution", ["720p", "1080p", "2K", "4K"]], ["engine", ["standard", "pro", "ultimate", "flashvsr", "seedance", "seedvr2", "runwayml/upscale-v1"]]], outputKeys: ["videoUrl"] },
|
|
4395
|
+
"userMessage": { fields: [["message", "string"]], outputKeys: ["content"] },
|
|
4396
|
+
"videoFaceSwap": { fields: [["videoUrl", "string"], ["faceImageUrl", "string"], ["targetIndex", "number"], ["engine", "string"]], outputKeys: ["videoUrl"] },
|
|
4397
|
+
"videoRemoveBackground": { fields: [["videoUrl", "string"], ["newBackground", ["transparent", "image"]], ["engine", "string"]], outputKeys: ["videoUrl"] },
|
|
4398
|
+
"videoRemoveWatermark": { fields: [["videoUrl", "string"], ["engine", "string"]], outputKeys: ["videoUrl"] },
|
|
4399
|
+
"watermarkImage": { fields: [["imageUrl", "string"], ["watermarkImageUrl", "string"], ["corner", ["top-left", "top-right", "bottom-left", "bottom-right"]], ["paddingPx", "number"], ["widthPx", "number"]], outputKeys: ["imageUrl"] },
|
|
4400
|
+
"watermarkVideo": { fields: [["videoUrl", "string"], ["imageUrl", "string"], ["corner", ["top-left", "top-right", "bottom-left", "bottom-right"]], ["paddingPx", "number"], ["widthPx", "number"]], outputKeys: ["videoUrl"] }
|
|
4401
|
+
};
|
|
4402
|
+
var blockTypeAliases = {
|
|
4403
|
+
"userMessage": "generateText",
|
|
4404
|
+
"generatePdf": "generateAsset"
|
|
4405
|
+
};
|
|
4406
|
+
|
|
4292
4407
|
// src/index.ts
|
|
4293
4408
|
var MindStudioAgent2 = MindStudioAgent;
|
|
4294
4409
|
var _default;
|