mastra 0.2.2-alpha.0 → 0.2.2
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/chunk-F6RK2UOG.js +912 -0
- package/package.json +3 -3
|
@@ -0,0 +1,912 @@
|
|
|
1
|
+
import * as p from '@clack/prompts';
|
|
2
|
+
import color2 from 'picocolors';
|
|
3
|
+
import child_process from 'node:child_process';
|
|
4
|
+
import util from 'node:util';
|
|
5
|
+
import path, { dirname } from 'path';
|
|
6
|
+
import prettier from 'prettier';
|
|
7
|
+
import yoctoSpinner from 'yocto-spinner';
|
|
8
|
+
import fsExtra3 from 'fs-extra/esm';
|
|
9
|
+
import fs4 from 'fs/promises';
|
|
10
|
+
import { execa } from 'execa';
|
|
11
|
+
import * as fs3 from 'fs';
|
|
12
|
+
import fs3__default from 'fs';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
import { createLogger } from '@mastra/core/logger';
|
|
15
|
+
|
|
16
|
+
// src/commands/create/create.ts
|
|
17
|
+
|
|
18
|
+
// src/commands/utils.ts
|
|
19
|
+
function getPackageManager() {
|
|
20
|
+
const userAgent = process.env.npm_config_user_agent || "";
|
|
21
|
+
const execPath = process.env.npm_execpath || "";
|
|
22
|
+
if (userAgent.includes("yarn")) {
|
|
23
|
+
return "yarn";
|
|
24
|
+
}
|
|
25
|
+
if (userAgent.includes("pnpm")) {
|
|
26
|
+
return "pnpm";
|
|
27
|
+
}
|
|
28
|
+
if (userAgent.includes("npm")) {
|
|
29
|
+
return "npm";
|
|
30
|
+
}
|
|
31
|
+
if (execPath.includes("yarn")) {
|
|
32
|
+
return "yarn";
|
|
33
|
+
}
|
|
34
|
+
if (execPath.includes("pnpm")) {
|
|
35
|
+
return "pnpm";
|
|
36
|
+
}
|
|
37
|
+
if (execPath.includes("npm")) {
|
|
38
|
+
return "npm";
|
|
39
|
+
}
|
|
40
|
+
return "npm";
|
|
41
|
+
}
|
|
42
|
+
var DepsService = class {
|
|
43
|
+
packageManager;
|
|
44
|
+
constructor() {
|
|
45
|
+
this.packageManager = this.getPackageManager();
|
|
46
|
+
}
|
|
47
|
+
findLockFile(dir) {
|
|
48
|
+
const lockFiles = ["pnpm-lock.yaml", "package-lock.json", "yarn.lock", "bun.lock"];
|
|
49
|
+
for (const file of lockFiles) {
|
|
50
|
+
if (fs3__default.existsSync(path.join(dir, file))) {
|
|
51
|
+
return file;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const parentDir = path.resolve(dir, "..");
|
|
55
|
+
if (parentDir !== dir) {
|
|
56
|
+
return this.findLockFile(parentDir);
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
getPackageManager() {
|
|
61
|
+
const lockFile = this.findLockFile(process.cwd());
|
|
62
|
+
switch (lockFile) {
|
|
63
|
+
case "pnpm-lock.yaml":
|
|
64
|
+
return "pnpm";
|
|
65
|
+
case "package-lock.json":
|
|
66
|
+
return "npm";
|
|
67
|
+
case "yarn.lock":
|
|
68
|
+
return "yarn";
|
|
69
|
+
case "bun.lock":
|
|
70
|
+
return "bun";
|
|
71
|
+
default:
|
|
72
|
+
return "npm";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async installPackages(packages) {
|
|
76
|
+
let runCommand = this.packageManager;
|
|
77
|
+
if (this.packageManager === "npm") {
|
|
78
|
+
runCommand = `${this.packageManager} i`;
|
|
79
|
+
} else {
|
|
80
|
+
runCommand = `${this.packageManager} add`;
|
|
81
|
+
}
|
|
82
|
+
const packageList = packages.join(" ");
|
|
83
|
+
return execa(`${runCommand} ${packageList}`, {
|
|
84
|
+
all: true,
|
|
85
|
+
shell: true,
|
|
86
|
+
stdio: "inherit"
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
async checkDependencies(dependencies) {
|
|
90
|
+
try {
|
|
91
|
+
const packageJsonPath = path.join(process.cwd(), "package.json");
|
|
92
|
+
try {
|
|
93
|
+
await fs4.access(packageJsonPath);
|
|
94
|
+
} catch {
|
|
95
|
+
return "No package.json file found in the current directory";
|
|
96
|
+
}
|
|
97
|
+
const packageJson = JSON.parse(await fs4.readFile(packageJsonPath, "utf-8"));
|
|
98
|
+
for (const dependency of dependencies) {
|
|
99
|
+
if (!packageJson.dependencies || !packageJson.dependencies[dependency]) {
|
|
100
|
+
return `Please install ${dependency} before running this command (${this.packageManager} install ${dependency})`;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return "ok";
|
|
104
|
+
} catch (err) {
|
|
105
|
+
console.error(err);
|
|
106
|
+
return "Could not check dependencies";
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async getProjectName() {
|
|
110
|
+
try {
|
|
111
|
+
const packageJsonPath = path.join(process.cwd(), "package.json");
|
|
112
|
+
const packageJson = await fs4.readFile(packageJsonPath, "utf-8");
|
|
113
|
+
const pkg = JSON.parse(packageJson);
|
|
114
|
+
return pkg.name;
|
|
115
|
+
} catch (err) {
|
|
116
|
+
throw err;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async getPackageVersion() {
|
|
120
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
121
|
+
const __dirname = dirname(__filename);
|
|
122
|
+
const pkgJsonPath = path.join(__dirname, "..", "package.json");
|
|
123
|
+
const content = await fsExtra3.readJSON(pkgJsonPath);
|
|
124
|
+
return content.version;
|
|
125
|
+
}
|
|
126
|
+
async addScriptsToPackageJson(scripts) {
|
|
127
|
+
const packageJson = JSON.parse(await fs4.readFile("package.json", "utf-8"));
|
|
128
|
+
packageJson.scripts = {
|
|
129
|
+
...packageJson.scripts,
|
|
130
|
+
...scripts
|
|
131
|
+
};
|
|
132
|
+
await fs4.writeFile("package.json", JSON.stringify(packageJson, null, 2));
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// src/services/service.env.ts
|
|
137
|
+
var EnvService = class {
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// src/services/service.fileEnv.ts
|
|
141
|
+
var FileEnvService = class extends EnvService {
|
|
142
|
+
filePath;
|
|
143
|
+
constructor(filePath) {
|
|
144
|
+
super();
|
|
145
|
+
this.filePath = filePath;
|
|
146
|
+
}
|
|
147
|
+
readFile(filePath) {
|
|
148
|
+
return new Promise((resolve, reject) => {
|
|
149
|
+
fs3.readFile(filePath, "utf8", (err, data) => {
|
|
150
|
+
if (err) reject(err);
|
|
151
|
+
else resolve(data);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
writeFile({ filePath, data }) {
|
|
156
|
+
return new Promise((resolve, reject) => {
|
|
157
|
+
fs3.writeFile(filePath, data, "utf8", (err) => {
|
|
158
|
+
if (err) reject(err);
|
|
159
|
+
else resolve();
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
async updateEnvData({
|
|
164
|
+
key,
|
|
165
|
+
value,
|
|
166
|
+
filePath = this.filePath,
|
|
167
|
+
data
|
|
168
|
+
}) {
|
|
169
|
+
const regex = new RegExp(`^${key}=.*$`, "m");
|
|
170
|
+
if (data.match(regex)) {
|
|
171
|
+
data = data.replace(regex, `${key}=${value}`);
|
|
172
|
+
} else {
|
|
173
|
+
data += `
|
|
174
|
+
${key}=${value}`;
|
|
175
|
+
}
|
|
176
|
+
await this.writeFile({ filePath, data });
|
|
177
|
+
console.log(`${key} set to ${value} in ENV file.`);
|
|
178
|
+
return data;
|
|
179
|
+
}
|
|
180
|
+
async getEnvValue(key) {
|
|
181
|
+
try {
|
|
182
|
+
const data = await this.readFile(this.filePath);
|
|
183
|
+
const regex = new RegExp(`^${key}=(.*)$`, "m");
|
|
184
|
+
const match = data.match(regex);
|
|
185
|
+
return match?.[1] || null;
|
|
186
|
+
} catch (err) {
|
|
187
|
+
console.error(`Error reading ENV value: ${err}`);
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
async setEnvValue(key, value) {
|
|
192
|
+
try {
|
|
193
|
+
const data = await this.readFile(this.filePath);
|
|
194
|
+
await this.updateEnvData({ key, value, data });
|
|
195
|
+
} catch (err) {
|
|
196
|
+
console.error(`Error writing ENV value: ${err}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
// src/services/service.file.ts
|
|
202
|
+
var FileService = class {
|
|
203
|
+
/**
|
|
204
|
+
*
|
|
205
|
+
* @param inputFile the file in the starter files directory to copy
|
|
206
|
+
* @param outputFilePath the destination path
|
|
207
|
+
* @param replaceIfExists flag to replace if it exists
|
|
208
|
+
* @returns
|
|
209
|
+
*/
|
|
210
|
+
async copyStarterFile(inputFile, outputFilePath, replaceIfExists) {
|
|
211
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
212
|
+
const __dirname = path.dirname(__filename);
|
|
213
|
+
const filePath = path.resolve(__dirname, "starter-files", inputFile);
|
|
214
|
+
const fileString = fs3__default.readFileSync(filePath, "utf8");
|
|
215
|
+
if (fs3__default.existsSync(outputFilePath) && !replaceIfExists) {
|
|
216
|
+
console.log(`${outputFilePath} already exists`);
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
await fsExtra3.outputFile(outputFilePath, fileString);
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
async setupEnvFile({ dbUrl }) {
|
|
223
|
+
const envPath = path.join(process.cwd(), ".env.development");
|
|
224
|
+
await fsExtra3.ensureFile(envPath);
|
|
225
|
+
const fileEnvService = new FileEnvService(envPath);
|
|
226
|
+
await fileEnvService.setEnvValue("DB_URL", dbUrl);
|
|
227
|
+
}
|
|
228
|
+
getFirstExistingFile(files) {
|
|
229
|
+
for (const f of files) {
|
|
230
|
+
if (fs3__default.existsSync(f)) {
|
|
231
|
+
return f;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
throw new Error("Missing required file, checked the following paths: " + files.join(", "));
|
|
235
|
+
}
|
|
236
|
+
replaceValuesInFile({
|
|
237
|
+
filePath,
|
|
238
|
+
replacements
|
|
239
|
+
}) {
|
|
240
|
+
let fileContent = fs3__default.readFileSync(filePath, "utf8");
|
|
241
|
+
replacements.forEach(({ search, replace }) => {
|
|
242
|
+
fileContent = fileContent.replaceAll(search, replace);
|
|
243
|
+
});
|
|
244
|
+
fs3__default.writeFileSync(filePath, fileContent);
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
var logger = createLogger({
|
|
248
|
+
name: "Mastra CLI",
|
|
249
|
+
level: "debug"
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// src/commands/init/utils.ts
|
|
253
|
+
var exec = util.promisify(child_process.exec);
|
|
254
|
+
var getAISDKPackage = (llmProvider) => {
|
|
255
|
+
switch (llmProvider) {
|
|
256
|
+
case "openai":
|
|
257
|
+
return "@ai-sdk/openai";
|
|
258
|
+
case "anthropic":
|
|
259
|
+
return "@ai-sdk/anthropic";
|
|
260
|
+
case "groq":
|
|
261
|
+
return "@ai-sdk/groq";
|
|
262
|
+
default:
|
|
263
|
+
return "@ai-sdk/openai";
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
var getProviderImportAndModelItem = (llmProvider) => {
|
|
267
|
+
let providerImport = "";
|
|
268
|
+
let modelItem = "";
|
|
269
|
+
if (llmProvider === "openai") {
|
|
270
|
+
providerImport = `import { openai } from '${getAISDKPackage(llmProvider)}';`;
|
|
271
|
+
modelItem = `openai('gpt-4o')`;
|
|
272
|
+
} else if (llmProvider === "anthropic") {
|
|
273
|
+
providerImport = `import { anthropic } from '${getAISDKPackage(llmProvider)}';`;
|
|
274
|
+
modelItem = `anthropic('claude-3-5-sonnet-20241022')`;
|
|
275
|
+
} else if (llmProvider === "groq") {
|
|
276
|
+
providerImport = `import { groq } from '${getAISDKPackage(llmProvider)}';`;
|
|
277
|
+
modelItem = `groq('llama3-groq-70b-8192-tool-use-preview')`;
|
|
278
|
+
}
|
|
279
|
+
return { providerImport, modelItem };
|
|
280
|
+
};
|
|
281
|
+
async function writeAgentSample(llmProvider, destPath, addExampleTool) {
|
|
282
|
+
const { providerImport, modelItem } = getProviderImportAndModelItem(llmProvider);
|
|
283
|
+
const instructions = `
|
|
284
|
+
You are a helpful weather assistant that provides accurate weather information.
|
|
285
|
+
|
|
286
|
+
Your primary function is to help users get weather details for specific locations. When responding:
|
|
287
|
+
- Always ask for a location if none is provided
|
|
288
|
+
- If giving a location with multiple parts (e.g. "New York, NY"), use the most relevant part (e.g. "New York")
|
|
289
|
+
- Include relevant details like humidity, wind conditions, and precipitation
|
|
290
|
+
- Keep responses concise but informative
|
|
291
|
+
|
|
292
|
+
${addExampleTool ? "Use the weatherTool to fetch current weather data." : ""}
|
|
293
|
+
`;
|
|
294
|
+
const content = `
|
|
295
|
+
${providerImport}
|
|
296
|
+
import { Agent } from '@mastra/core/agent';
|
|
297
|
+
${addExampleTool ? `import { weatherTool } from '../tools';` : ""}
|
|
298
|
+
|
|
299
|
+
export const weatherAgent = new Agent({
|
|
300
|
+
name: 'Weather Agent',
|
|
301
|
+
instructions: \`${instructions}\`,
|
|
302
|
+
model: ${modelItem},
|
|
303
|
+
${addExampleTool ? "tools: { weatherTool }," : ""}
|
|
304
|
+
});
|
|
305
|
+
`;
|
|
306
|
+
const formattedContent = await prettier.format(content, {
|
|
307
|
+
parser: "typescript",
|
|
308
|
+
singleQuote: true
|
|
309
|
+
});
|
|
310
|
+
await fs4.writeFile(destPath, "");
|
|
311
|
+
await fs4.writeFile(destPath, formattedContent);
|
|
312
|
+
}
|
|
313
|
+
async function writeWorkflowSample(destPath, llmProvider) {
|
|
314
|
+
const { providerImport, modelItem } = getProviderImportAndModelItem(llmProvider);
|
|
315
|
+
const content = `${providerImport}
|
|
316
|
+
import { Agent } from '@mastra/core/agent';
|
|
317
|
+
import { Step, Workflow } from '@mastra/core/workflows';
|
|
318
|
+
import { z } from 'zod';
|
|
319
|
+
|
|
320
|
+
const llm = ${modelItem};
|
|
321
|
+
|
|
322
|
+
const agent = new Agent({
|
|
323
|
+
name: 'Weather Agent',
|
|
324
|
+
model: llm,
|
|
325
|
+
instructions: \`
|
|
326
|
+
You are a local activities and travel expert who excels at weather-based planning. Analyze the weather data and provide practical activity recommendations.
|
|
327
|
+
|
|
328
|
+
For each day in the forecast, structure your response exactly as follows:
|
|
329
|
+
|
|
330
|
+
\u{1F4C5} [Day, Month Date, Year]
|
|
331
|
+
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
332
|
+
|
|
333
|
+
\u{1F321}\uFE0F WEATHER SUMMARY
|
|
334
|
+
\u2022 Conditions: [brief description]
|
|
335
|
+
\u2022 Temperature: [X\xB0C/Y\xB0F to A\xB0C/B\xB0F]
|
|
336
|
+
\u2022 Precipitation: [X% chance]
|
|
337
|
+
|
|
338
|
+
\u{1F305} MORNING ACTIVITIES
|
|
339
|
+
Outdoor:
|
|
340
|
+
\u2022 [Activity Name] - [Brief description including specific location/route]
|
|
341
|
+
Best timing: [specific time range]
|
|
342
|
+
Note: [relevant weather consideration]
|
|
343
|
+
|
|
344
|
+
\u{1F31E} AFTERNOON ACTIVITIES
|
|
345
|
+
Outdoor:
|
|
346
|
+
\u2022 [Activity Name] - [Brief description including specific location/route]
|
|
347
|
+
Best timing: [specific time range]
|
|
348
|
+
Note: [relevant weather consideration]
|
|
349
|
+
|
|
350
|
+
\u{1F3E0} INDOOR ALTERNATIVES
|
|
351
|
+
\u2022 [Activity Name] - [Brief description including specific venue]
|
|
352
|
+
Ideal for: [weather condition that would trigger this alternative]
|
|
353
|
+
|
|
354
|
+
\u26A0\uFE0F SPECIAL CONSIDERATIONS
|
|
355
|
+
\u2022 [Any relevant weather warnings, UV index, wind conditions, etc.]
|
|
356
|
+
|
|
357
|
+
Guidelines:
|
|
358
|
+
- Suggest 2-3 time-specific outdoor activities per day
|
|
359
|
+
- Include 1-2 indoor backup options
|
|
360
|
+
- For precipitation >50%, lead with indoor activities
|
|
361
|
+
- All activities must be specific to the location
|
|
362
|
+
- Include specific venues, trails, or locations
|
|
363
|
+
- Consider activity intensity based on temperature
|
|
364
|
+
- Keep descriptions concise but informative
|
|
365
|
+
|
|
366
|
+
Maintain this exact formatting for consistency, using the emoji and section headers as shown.
|
|
367
|
+
\`,
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
const fetchWeather = new Step({
|
|
371
|
+
id: 'fetch-weather',
|
|
372
|
+
description: 'Fetches weather forecast for a given city',
|
|
373
|
+
inputSchema: z.object({
|
|
374
|
+
city: z.string().describe('The city to get the weather for'),
|
|
375
|
+
}),
|
|
376
|
+
execute: async ({ context }) => {
|
|
377
|
+
const triggerData = context?.getStepPayload<{ city: string }>('trigger');
|
|
378
|
+
|
|
379
|
+
if (!triggerData) {
|
|
380
|
+
throw new Error('Trigger data not found');
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const geocodingUrl = \`https://geocoding-api.open-meteo.com/v1/search?name=\${encodeURIComponent(triggerData.city)}&count=1\`;
|
|
384
|
+
const geocodingResponse = await fetch(geocodingUrl);
|
|
385
|
+
const geocodingData = (await geocodingResponse.json()) as {
|
|
386
|
+
results: { latitude: number; longitude: number; name: string }[];
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
if (!geocodingData.results?.[0]) {
|
|
390
|
+
throw new Error(\`Location '\${triggerData.city}' not found\`);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const { latitude, longitude, name } = geocodingData.results[0];
|
|
394
|
+
|
|
395
|
+
const weatherUrl = \`https://api.open-meteo.com/v1/forecast?latitude=\${latitude}&longitude=\${longitude}&daily=temperature_2m_max,temperature_2m_min,precipitation_probability_mean,weathercode&timezone=auto\`;
|
|
396
|
+
const response = await fetch(weatherUrl);
|
|
397
|
+
const data = (await response.json()) as {
|
|
398
|
+
daily: {
|
|
399
|
+
time: string[];
|
|
400
|
+
temperature_2m_max: number[];
|
|
401
|
+
temperature_2m_min: number[];
|
|
402
|
+
precipitation_probability_mean: number[];
|
|
403
|
+
weathercode: number[];
|
|
404
|
+
};
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
const forecast = data.daily.time.map((date: string, index: number) => ({
|
|
408
|
+
date,
|
|
409
|
+
maxTemp: data.daily.temperature_2m_max[index],
|
|
410
|
+
minTemp: data.daily.temperature_2m_min[index],
|
|
411
|
+
precipitationChance: data.daily.precipitation_probability_mean[index],
|
|
412
|
+
condition: getWeatherCondition(data.daily.weathercode[index]!),
|
|
413
|
+
location: name,
|
|
414
|
+
}));
|
|
415
|
+
|
|
416
|
+
return forecast;
|
|
417
|
+
},
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
const forecastSchema = z.array(
|
|
421
|
+
z.object({
|
|
422
|
+
date: z.string(),
|
|
423
|
+
maxTemp: z.number(),
|
|
424
|
+
minTemp: z.number(),
|
|
425
|
+
precipitationChance: z.number(),
|
|
426
|
+
condition: z.string(),
|
|
427
|
+
location: z.string(),
|
|
428
|
+
}),
|
|
429
|
+
);
|
|
430
|
+
|
|
431
|
+
const planActivities = new Step({
|
|
432
|
+
id: 'plan-activities',
|
|
433
|
+
description: 'Suggests activities based on weather conditions',
|
|
434
|
+
inputSchema: forecastSchema,
|
|
435
|
+
execute: async ({ context, mastra }) => {
|
|
436
|
+
const forecast = context?.getStepPayload<z.infer<typeof forecastSchema>>('fetch-weather');
|
|
437
|
+
|
|
438
|
+
if (!forecast || forecast.length === 0) {
|
|
439
|
+
throw new Error('Forecast data not found');
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const prompt = \`Based on the following weather forecast for \${forecast[0]?.location}, suggest appropriate activities:
|
|
443
|
+
\${JSON.stringify(forecast, null, 2)}
|
|
444
|
+
\`;
|
|
445
|
+
|
|
446
|
+
const response = await agent.stream([
|
|
447
|
+
{
|
|
448
|
+
role: 'user',
|
|
449
|
+
content: prompt,
|
|
450
|
+
},
|
|
451
|
+
]);
|
|
452
|
+
|
|
453
|
+
for await (const chunk of response.textStream) {
|
|
454
|
+
process.stdout.write(chunk);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return {
|
|
458
|
+
activities: response.text,
|
|
459
|
+
};
|
|
460
|
+
},
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
function getWeatherCondition(code: number): string {
|
|
464
|
+
const conditions: Record<number, string> = {
|
|
465
|
+
0: 'Clear sky',
|
|
466
|
+
1: 'Mainly clear',
|
|
467
|
+
2: 'Partly cloudy',
|
|
468
|
+
3: 'Overcast',
|
|
469
|
+
45: 'Foggy',
|
|
470
|
+
48: 'Depositing rime fog',
|
|
471
|
+
51: 'Light drizzle',
|
|
472
|
+
53: 'Moderate drizzle',
|
|
473
|
+
55: 'Dense drizzle',
|
|
474
|
+
61: 'Slight rain',
|
|
475
|
+
63: 'Moderate rain',
|
|
476
|
+
65: 'Heavy rain',
|
|
477
|
+
71: 'Slight snow fall',
|
|
478
|
+
73: 'Moderate snow fall',
|
|
479
|
+
75: 'Heavy snow fall',
|
|
480
|
+
95: 'Thunderstorm',
|
|
481
|
+
};
|
|
482
|
+
return conditions[code] || 'Unknown';
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const weatherWorkflow = new Workflow({
|
|
486
|
+
name: 'weather-workflow',
|
|
487
|
+
triggerSchema: z.object({
|
|
488
|
+
city: z.string().describe('The city to get the weather for'),
|
|
489
|
+
}),
|
|
490
|
+
})
|
|
491
|
+
.step(fetchWeather)
|
|
492
|
+
.then(planActivities);
|
|
493
|
+
|
|
494
|
+
weatherWorkflow.commit();
|
|
495
|
+
|
|
496
|
+
export { weatherWorkflow };`;
|
|
497
|
+
const formattedContent = await prettier.format(content, {
|
|
498
|
+
parser: "typescript",
|
|
499
|
+
semi: true,
|
|
500
|
+
singleQuote: true
|
|
501
|
+
});
|
|
502
|
+
await fs4.writeFile(destPath, formattedContent);
|
|
503
|
+
}
|
|
504
|
+
async function writeToolSample(destPath) {
|
|
505
|
+
const fileService = new FileService();
|
|
506
|
+
await fileService.copyStarterFile("tools.ts", destPath);
|
|
507
|
+
}
|
|
508
|
+
async function writeCodeSampleForComponents(llmprovider, component, destPath, importComponents) {
|
|
509
|
+
switch (component) {
|
|
510
|
+
case "agents":
|
|
511
|
+
return writeAgentSample(llmprovider, destPath, importComponents.includes("tools"));
|
|
512
|
+
case "tools":
|
|
513
|
+
return writeToolSample(destPath);
|
|
514
|
+
case "workflows":
|
|
515
|
+
return writeWorkflowSample(destPath, llmprovider);
|
|
516
|
+
default:
|
|
517
|
+
return "";
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
var createComponentsDir = async (dirPath, component) => {
|
|
521
|
+
const componentPath = dirPath + `/${component}`;
|
|
522
|
+
await fsExtra3.ensureDir(componentPath);
|
|
523
|
+
};
|
|
524
|
+
var writeIndexFile = async ({
|
|
525
|
+
dirPath,
|
|
526
|
+
addAgent,
|
|
527
|
+
addExample,
|
|
528
|
+
addWorkflow
|
|
529
|
+
}) => {
|
|
530
|
+
const indexPath = dirPath + "/index.ts";
|
|
531
|
+
const destPath = path.join(indexPath);
|
|
532
|
+
try {
|
|
533
|
+
await fs4.writeFile(destPath, "");
|
|
534
|
+
const filteredExports = [
|
|
535
|
+
addWorkflow ? `workflows: { weatherWorkflow },` : "",
|
|
536
|
+
addAgent ? `agents: { weatherAgent },` : ""
|
|
537
|
+
].filter(Boolean);
|
|
538
|
+
if (!addExample) {
|
|
539
|
+
await fs4.writeFile(
|
|
540
|
+
destPath,
|
|
541
|
+
`
|
|
542
|
+
import { Mastra } from '@mastra/core';
|
|
543
|
+
|
|
544
|
+
export const mastra = new Mastra()
|
|
545
|
+
`
|
|
546
|
+
);
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
await fs4.writeFile(
|
|
550
|
+
destPath,
|
|
551
|
+
`
|
|
552
|
+
import { Mastra } from '@mastra/core/mastra';
|
|
553
|
+
import { createLogger } from '@mastra/core/logger';
|
|
554
|
+
${addWorkflow ? `import { weatherWorkflow } from './workflows';` : ""}
|
|
555
|
+
${addAgent ? `import { weatherAgent } from './agents';` : ""}
|
|
556
|
+
|
|
557
|
+
export const mastra = new Mastra({
|
|
558
|
+
${filteredExports.join("\n ")}
|
|
559
|
+
logger: createLogger({
|
|
560
|
+
name: 'Mastra',
|
|
561
|
+
level: 'info',
|
|
562
|
+
}),
|
|
563
|
+
});
|
|
564
|
+
`
|
|
565
|
+
);
|
|
566
|
+
} catch (err) {
|
|
567
|
+
throw err;
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
var checkAndInstallCoreDeps = async () => {
|
|
571
|
+
const depsService = new DepsService();
|
|
572
|
+
const depCheck = await depsService.checkDependencies(["@mastra/core"]);
|
|
573
|
+
if (depCheck !== "ok") {
|
|
574
|
+
await installCoreDeps();
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
var spinner = yoctoSpinner({ text: "Installing Mastra core dependencies\n" });
|
|
578
|
+
async function installCoreDeps() {
|
|
579
|
+
try {
|
|
580
|
+
const confirm2 = await p.confirm({
|
|
581
|
+
message: "You do not have the @mastra/core package installed. Would you like to install it?",
|
|
582
|
+
initialValue: false
|
|
583
|
+
});
|
|
584
|
+
if (p.isCancel(confirm2)) {
|
|
585
|
+
p.cancel("Installation Cancelled");
|
|
586
|
+
process.exit(0);
|
|
587
|
+
}
|
|
588
|
+
if (!confirm2) {
|
|
589
|
+
p.cancel("Installation Cancelled");
|
|
590
|
+
process.exit(0);
|
|
591
|
+
}
|
|
592
|
+
spinner.start();
|
|
593
|
+
const depsService = new DepsService();
|
|
594
|
+
await depsService.installPackages(["@mastra/core@latest"]);
|
|
595
|
+
spinner.success("@mastra/core installed successfully");
|
|
596
|
+
} catch (err) {
|
|
597
|
+
console.error(err);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
var getAPIKey = async (provider) => {
|
|
601
|
+
let key = "OPENAI_API_KEY";
|
|
602
|
+
switch (provider) {
|
|
603
|
+
case "anthropic":
|
|
604
|
+
key = "ANTHROPIC_API_KEY";
|
|
605
|
+
return key;
|
|
606
|
+
case "groq":
|
|
607
|
+
key = "GROQ_API_KEY";
|
|
608
|
+
return key;
|
|
609
|
+
default:
|
|
610
|
+
return key;
|
|
611
|
+
}
|
|
612
|
+
};
|
|
613
|
+
var writeAPIKey = async ({
|
|
614
|
+
provider,
|
|
615
|
+
apiKey = "your-api-key"
|
|
616
|
+
}) => {
|
|
617
|
+
const key = await getAPIKey(provider);
|
|
618
|
+
await exec(`echo ${key}=${apiKey} >> .env.development`);
|
|
619
|
+
};
|
|
620
|
+
var createMastraDir = async (directory) => {
|
|
621
|
+
let dir = directory.trim().split("/").filter((item) => item !== "");
|
|
622
|
+
const dirPath = path.join(process.cwd(), ...dir, "mastra");
|
|
623
|
+
try {
|
|
624
|
+
await fs4.access(dirPath);
|
|
625
|
+
return { ok: false };
|
|
626
|
+
} catch {
|
|
627
|
+
await fsExtra3.ensureDir(dirPath);
|
|
628
|
+
return { ok: true, dirPath };
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
var writeCodeSample = async (dirPath, component, llmProvider, importComponents) => {
|
|
632
|
+
const destPath = dirPath + `/${component}/index.ts`;
|
|
633
|
+
try {
|
|
634
|
+
await writeCodeSampleForComponents(llmProvider, component, destPath, importComponents);
|
|
635
|
+
} catch (err) {
|
|
636
|
+
throw err;
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
var interactivePrompt = async () => {
|
|
640
|
+
p.intro(color2.inverse("Mastra Init"));
|
|
641
|
+
const mastraProject = await p.group(
|
|
642
|
+
{
|
|
643
|
+
directory: () => p.text({
|
|
644
|
+
message: "Where should we create the Mastra files? (default: src/)",
|
|
645
|
+
placeholder: "src/",
|
|
646
|
+
defaultValue: "src/"
|
|
647
|
+
}),
|
|
648
|
+
components: () => p.multiselect({
|
|
649
|
+
message: "Choose components to install:",
|
|
650
|
+
options: [
|
|
651
|
+
{ value: "agents", label: "Agents", hint: "recommended" },
|
|
652
|
+
{
|
|
653
|
+
value: "workflows",
|
|
654
|
+
label: "Workflows"
|
|
655
|
+
}
|
|
656
|
+
]
|
|
657
|
+
}),
|
|
658
|
+
shouldAddTools: () => p.confirm({
|
|
659
|
+
message: "Add tools?",
|
|
660
|
+
initialValue: false
|
|
661
|
+
}),
|
|
662
|
+
llmProvider: () => p.select({
|
|
663
|
+
message: "Select default provider:",
|
|
664
|
+
options: [
|
|
665
|
+
{ value: "openai", label: "OpenAI", hint: "recommended" },
|
|
666
|
+
{ value: "anthropic", label: "Anthropic" },
|
|
667
|
+
{ value: "groq", label: "Groq" }
|
|
668
|
+
]
|
|
669
|
+
}),
|
|
670
|
+
llmApiKey: async ({ results: { llmProvider } }) => {
|
|
671
|
+
const keyChoice = await p.select({
|
|
672
|
+
message: `Enter your ${llmProvider} API key?`,
|
|
673
|
+
options: [
|
|
674
|
+
{ value: "skip", label: "Skip for now", hint: "default" },
|
|
675
|
+
{ value: "enter", label: "Enter API key" }
|
|
676
|
+
],
|
|
677
|
+
initialValue: "skip"
|
|
678
|
+
});
|
|
679
|
+
if (keyChoice === "enter") {
|
|
680
|
+
return p.text({
|
|
681
|
+
message: "Enter your API key:",
|
|
682
|
+
placeholder: "sk-..."
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
return void 0;
|
|
686
|
+
},
|
|
687
|
+
addExample: () => p.confirm({
|
|
688
|
+
message: "Add example",
|
|
689
|
+
initialValue: false
|
|
690
|
+
})
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
onCancel: () => {
|
|
694
|
+
p.cancel("Operation cancelled.");
|
|
695
|
+
process.exit(0);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
);
|
|
699
|
+
const { shouldAddTools, components, ...rest } = mastraProject;
|
|
700
|
+
const mastraComponents = shouldAddTools ? [...components, "tools"] : components;
|
|
701
|
+
return { ...rest, components: mastraComponents };
|
|
702
|
+
};
|
|
703
|
+
var checkPkgJson = async () => {
|
|
704
|
+
const cwd = process.cwd();
|
|
705
|
+
const pkgJsonPath = path.join(cwd, "package.json");
|
|
706
|
+
let isPkgJsonPresent = false;
|
|
707
|
+
try {
|
|
708
|
+
await fsExtra3.readJSON(pkgJsonPath);
|
|
709
|
+
isPkgJsonPresent = true;
|
|
710
|
+
} catch (err) {
|
|
711
|
+
isPkgJsonPresent = false;
|
|
712
|
+
}
|
|
713
|
+
if (isPkgJsonPresent) {
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
logger.debug('package.json not found, create one or run "mastra create" to create a new project');
|
|
717
|
+
process.exit(0);
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
// src/commands/init/init.ts
|
|
721
|
+
var s = p.spinner();
|
|
722
|
+
var exec2 = util.promisify(child_process.exec);
|
|
723
|
+
var init = async ({
|
|
724
|
+
directory,
|
|
725
|
+
addExample = false,
|
|
726
|
+
components,
|
|
727
|
+
llmProvider = "openai",
|
|
728
|
+
llmApiKey
|
|
729
|
+
}) => {
|
|
730
|
+
s.start("Initializing Mastra");
|
|
731
|
+
try {
|
|
732
|
+
const result = await createMastraDir(directory);
|
|
733
|
+
if (!result.ok) {
|
|
734
|
+
s.stop(color2.inverse(" Mastra already initialized "));
|
|
735
|
+
return { success: false };
|
|
736
|
+
}
|
|
737
|
+
const dirPath = result.dirPath;
|
|
738
|
+
await Promise.all([
|
|
739
|
+
writeIndexFile({
|
|
740
|
+
dirPath,
|
|
741
|
+
addExample,
|
|
742
|
+
addWorkflow: components.includes("workflows"),
|
|
743
|
+
addAgent: components.includes("agents")
|
|
744
|
+
}),
|
|
745
|
+
...components.map((component) => createComponentsDir(dirPath, component)),
|
|
746
|
+
writeAPIKey({ provider: llmProvider, apiKey: llmApiKey })
|
|
747
|
+
]);
|
|
748
|
+
if (addExample) {
|
|
749
|
+
await Promise.all([
|
|
750
|
+
...components.map(
|
|
751
|
+
(component) => writeCodeSample(dirPath, component, llmProvider, components)
|
|
752
|
+
)
|
|
753
|
+
]);
|
|
754
|
+
}
|
|
755
|
+
const key = await getAPIKey(llmProvider || "openai");
|
|
756
|
+
const aiSdkPackage = getAISDKPackage(llmProvider);
|
|
757
|
+
await exec2(`${getPackageManager()} i ${aiSdkPackage}`);
|
|
758
|
+
s.stop();
|
|
759
|
+
if (!llmApiKey) {
|
|
760
|
+
p.note(`
|
|
761
|
+
${color2.green("Mastra initialized successfully!")}
|
|
762
|
+
|
|
763
|
+
Add your ${color2.cyan(key)} as an environment variable
|
|
764
|
+
in your ${color2.cyan(".env.development")} file
|
|
765
|
+
`);
|
|
766
|
+
} else {
|
|
767
|
+
p.note(`
|
|
768
|
+
${color2.green("Mastra initialized successfully!")}
|
|
769
|
+
`);
|
|
770
|
+
}
|
|
771
|
+
return { success: true };
|
|
772
|
+
} catch (err) {
|
|
773
|
+
s.stop(color2.inverse("An error occurred while initializing Mastra"));
|
|
774
|
+
console.error(err);
|
|
775
|
+
return { success: false };
|
|
776
|
+
}
|
|
777
|
+
};
|
|
778
|
+
var exec3 = util.promisify(child_process.exec);
|
|
779
|
+
var execWithTimeout = async (command, timeoutMs = 18e4) => {
|
|
780
|
+
try {
|
|
781
|
+
const promise = exec3(command, { killSignal: "SIGTERM" });
|
|
782
|
+
let timeoutId;
|
|
783
|
+
const timeout = new Promise((_, reject) => {
|
|
784
|
+
timeoutId = setTimeout(() => reject(new Error("Command timed out")), timeoutMs);
|
|
785
|
+
});
|
|
786
|
+
try {
|
|
787
|
+
const result = await Promise.race([promise, timeout]);
|
|
788
|
+
clearTimeout(timeoutId);
|
|
789
|
+
return result;
|
|
790
|
+
} catch (error) {
|
|
791
|
+
clearTimeout(timeoutId);
|
|
792
|
+
if (error instanceof Error && error.message === "Command timed out") {
|
|
793
|
+
throw new Error("Something went wrong during installation, please try again.");
|
|
794
|
+
}
|
|
795
|
+
throw error;
|
|
796
|
+
}
|
|
797
|
+
} catch (error) {
|
|
798
|
+
console.error(error);
|
|
799
|
+
throw error;
|
|
800
|
+
}
|
|
801
|
+
};
|
|
802
|
+
var createMastraProject = async () => {
|
|
803
|
+
p.intro(color2.inverse("Mastra Create"));
|
|
804
|
+
const projectName = await p.text({
|
|
805
|
+
message: "What do you want to name your project?",
|
|
806
|
+
placeholder: "my-mastra-app",
|
|
807
|
+
defaultValue: "my-mastra-app"
|
|
808
|
+
});
|
|
809
|
+
if (p.isCancel(projectName)) {
|
|
810
|
+
p.cancel("Operation cancelled");
|
|
811
|
+
process.exit(0);
|
|
812
|
+
}
|
|
813
|
+
const s2 = p.spinner();
|
|
814
|
+
s2.start("Creating project");
|
|
815
|
+
try {
|
|
816
|
+
await fs4.mkdir(projectName);
|
|
817
|
+
} catch (error) {
|
|
818
|
+
if (error instanceof Error && "code" in error && error.code === "EEXIST") {
|
|
819
|
+
s2.stop(
|
|
820
|
+
`A directory named "${projectName}" already exists. Please choose a different name or delete the existing directory.`
|
|
821
|
+
);
|
|
822
|
+
process.exit(1);
|
|
823
|
+
}
|
|
824
|
+
throw error;
|
|
825
|
+
}
|
|
826
|
+
process.chdir(projectName);
|
|
827
|
+
const pm = getPackageManager();
|
|
828
|
+
s2.message("Creating project");
|
|
829
|
+
await exec3(`npm init -y`);
|
|
830
|
+
await exec3(`npm pkg set type="module"`);
|
|
831
|
+
const depsService = new DepsService();
|
|
832
|
+
await depsService.addScriptsToPackageJson({
|
|
833
|
+
dev: "mastra dev"
|
|
834
|
+
});
|
|
835
|
+
s2.stop("Project created");
|
|
836
|
+
s2.start(`Installing ${pm} dependencies`);
|
|
837
|
+
await exec3(`${pm} i zod`);
|
|
838
|
+
await exec3(`${pm} i typescript tsx @types/node --save-dev`);
|
|
839
|
+
await exec3(`echo '{
|
|
840
|
+
"compilerOptions": {
|
|
841
|
+
"target": "ES2022",
|
|
842
|
+
"module": "ES2022",
|
|
843
|
+
"moduleResolution": "bundler",
|
|
844
|
+
"esModuleInterop": true,
|
|
845
|
+
"forceConsistentCasingInFileNames": true,
|
|
846
|
+
"strict": true,
|
|
847
|
+
"skipLibCheck": true,
|
|
848
|
+
"outDir": "dist"
|
|
849
|
+
},
|
|
850
|
+
"include": [
|
|
851
|
+
"src/**/*"
|
|
852
|
+
],
|
|
853
|
+
"exclude": [
|
|
854
|
+
"node_modules",
|
|
855
|
+
"dist",
|
|
856
|
+
".mastra"
|
|
857
|
+
]
|
|
858
|
+
}' > tsconfig.json`);
|
|
859
|
+
s2.stop(`${pm} dependencies installed`);
|
|
860
|
+
s2.start("Installing mastra");
|
|
861
|
+
await execWithTimeout(`${pm} i -D mastra@latest`);
|
|
862
|
+
s2.stop("mastra installed");
|
|
863
|
+
s2.start("Installing @mastra/core");
|
|
864
|
+
await execWithTimeout(`${pm} i @mastra/core@latest`);
|
|
865
|
+
s2.stop("@mastra/core installed");
|
|
866
|
+
s2.start("Adding .gitignore");
|
|
867
|
+
await exec3(`echo output.txt >> .gitignore`);
|
|
868
|
+
await exec3(`echo node_modules >> .gitignore`);
|
|
869
|
+
await exec3(`echo dist >> .gitignore`);
|
|
870
|
+
await exec3(`echo .mastra >> .gitignore`);
|
|
871
|
+
await exec3(`echo .env.development >> .gitignore`);
|
|
872
|
+
await exec3(`echo .env >> .gitignore`);
|
|
873
|
+
await exec3(`echo *.db >> .gitignore`);
|
|
874
|
+
s2.stop(".gitignore added");
|
|
875
|
+
p.outro("Project created successfully");
|
|
876
|
+
console.log("");
|
|
877
|
+
return { projectName };
|
|
878
|
+
};
|
|
879
|
+
|
|
880
|
+
// src/commands/create/create.ts
|
|
881
|
+
var create = async (args) => {
|
|
882
|
+
const { projectName } = await createMastraProject();
|
|
883
|
+
const directory = "/src";
|
|
884
|
+
if (!args.components || !args.llmProvider || !args.addExample) {
|
|
885
|
+
const result = await interactivePrompt();
|
|
886
|
+
await init({
|
|
887
|
+
...result,
|
|
888
|
+
llmApiKey: result?.llmApiKey
|
|
889
|
+
});
|
|
890
|
+
postCreate({ projectName });
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
const { components = [], llmProvider = "openai", addExample = false, llmApiKey } = args;
|
|
894
|
+
await init({
|
|
895
|
+
directory,
|
|
896
|
+
components,
|
|
897
|
+
llmProvider,
|
|
898
|
+
addExample,
|
|
899
|
+
llmApiKey
|
|
900
|
+
});
|
|
901
|
+
postCreate({ projectName });
|
|
902
|
+
};
|
|
903
|
+
var postCreate = ({ projectName }) => {
|
|
904
|
+
p.outro(`
|
|
905
|
+
${color2.green("To start your project:")}
|
|
906
|
+
|
|
907
|
+
${color2.cyan("cd")} ${projectName}
|
|
908
|
+
${color2.cyan("npm run dev")}
|
|
909
|
+
`);
|
|
910
|
+
};
|
|
911
|
+
|
|
912
|
+
export { DepsService, FileService, checkAndInstallCoreDeps, checkPkgJson, create, init, interactivePrompt, logger };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mastra",
|
|
3
|
-
"version": "0.2.2
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "cli for mastra",
|
|
6
6
|
"type": "module",
|
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
"yocto-spinner": "^0.1.1",
|
|
56
56
|
"zod": "^3.24.1",
|
|
57
57
|
"zod-to-json-schema": "^3.24.1",
|
|
58
|
-
"@mastra/
|
|
59
|
-
"@mastra/
|
|
58
|
+
"@mastra/core": "^0.2.1",
|
|
59
|
+
"@mastra/deployer": "^0.1.1"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@ai-sdk/openai": "^1.1.9",
|