UNPKG

mastra

Version:

cli for mastra

1,067 lines (1,019 loc) • 33 kB
import * as p from '@clack/prompts'; import color2 from 'picocolors'; import child_process from 'node:child_process'; import util from 'node:util'; import * as fs3 from 'fs'; import fs3__default, { existsSync } from 'fs'; import fs4 from 'fs/promises'; import path, { dirname } from 'path'; import { fileURLToPath } from 'url'; import { execa } from 'execa'; import fsExtra3, { readJSON, ensureFile, writeJSON } from 'fs-extra/esm'; import os from 'os'; import prettier from 'prettier'; import yoctoSpinner from 'yocto-spinner'; import { createLogger } from '@mastra/core/logger'; // src/commands/create/create.ts var DepsService = class { packageManager; constructor() { this.packageManager = this.getPackageManager(); } findLockFile(dir) { const lockFiles = ["pnpm-lock.yaml", "package-lock.json", "yarn.lock", "bun.lock"]; for (const file of lockFiles) { if (fs3__default.existsSync(path.join(dir, file))) { return file; } } const parentDir = path.resolve(dir, ".."); if (parentDir !== dir) { return this.findLockFile(parentDir); } return null; } getPackageManager() { const lockFile = this.findLockFile(process.cwd()); switch (lockFile) { case "pnpm-lock.yaml": return "pnpm"; case "package-lock.json": return "npm"; case "yarn.lock": return "yarn"; case "bun.lock": return "bun"; default: return "npm"; } } async installPackages(packages) { let runCommand = this.packageManager; if (this.packageManager === "npm") { runCommand = `${this.packageManager} i`; } else { runCommand = `${this.packageManager} add`; } const packageList = packages.join(" "); return execa(`${runCommand} ${packageList}`, { all: true, shell: true, stdio: "inherit" }); } async checkDependencies(dependencies) { try { const packageJsonPath = path.join(process.cwd(), "package.json"); try { await fs4.access(packageJsonPath); } catch { return "No package.json file found in the current directory"; } const packageJson = JSON.parse(await fs4.readFile(packageJsonPath, "utf-8")); for (const dependency of dependencies) { if (!packageJson.dependencies || !packageJson.dependencies[dependency]) { return `Please install ${dependency} before running this command (${this.packageManager} install ${dependency})`; } } return "ok"; } catch (err) { console.error(err); return "Could not check dependencies"; } } async getProjectName() { try { const packageJsonPath = path.join(process.cwd(), "package.json"); const packageJson = await fs4.readFile(packageJsonPath, "utf-8"); const pkg = JSON.parse(packageJson); return pkg.name; } catch (err) { throw err; } } async getPackageVersion() { const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const pkgJsonPath = path.join(__dirname, "..", "package.json"); const content = await fsExtra3.readJSON(pkgJsonPath); return content.version; } async addScriptsToPackageJson(scripts) { const packageJson = JSON.parse(await fs4.readFile("package.json", "utf-8")); packageJson.scripts = { ...packageJson.scripts, ...scripts }; await fs4.writeFile("package.json", JSON.stringify(packageJson, null, 2)); } }; // src/commands/utils.ts function getPackageManager() { const userAgent = process.env.npm_config_user_agent || ""; const execPath = process.env.npm_execpath || ""; if (userAgent.includes("yarn")) { return "yarn"; } if (userAgent.includes("pnpm")) { return "pnpm"; } if (userAgent.includes("npm")) { return "npm"; } if (execPath.includes("yarn")) { return "yarn"; } if (execPath.includes("pnpm")) { return "pnpm"; } if (execPath.includes("npm")) { return "npm"; } return "npm"; } function getPackageManagerInstallCommand(pm) { switch (pm) { case "npm": return "install"; case "yarn": return "add"; case "pnpm": return "add"; default: return "install"; } } var args = ["-y", "@mastra/mcp-docs-server@latest"]; var mcpConfig = { mcpServers: { mastra: process.platform === `win32` ? { command: "cmd", args: ["/c", "npx", ...args] } : { command: "npx", args } } }; function makeConfig(original) { return { ...original, mcpServers: { ...original?.mcpServers || {}, ...mcpConfig.mcpServers } }; } async function writeMergedConfig(configPath) { const configExists = existsSync(configPath); const config = makeConfig(configExists ? await readJSON(configPath) : {}); await ensureFile(configPath); await writeJSON(configPath, config, { spaces: 2 }); } var windsurfGlobalMCPConfigPath = path.join(os.homedir(), ".codeium", "windsurf", "mcp_config.json"); async function installMastraDocsMCPServer({ editor, directory }) { if (editor === `cursor`) await writeMergedConfig(path.join(directory, ".cursor", "mcp.json")); const windsurfIsInstalled = await globalWindsurfMCPIsAlreadyInstalled(); if (editor === `windsurf` && !windsurfIsInstalled) await writeMergedConfig(windsurfGlobalMCPConfigPath); } async function globalWindsurfMCPIsAlreadyInstalled() { if (!existsSync(windsurfGlobalMCPConfigPath)) { return false; } try { const configContents = await readJSON(windsurfGlobalMCPConfigPath); if (!configContents?.mcpServers) return false; const hasMastraMCP = Object.values(configContents.mcpServers).some( (server) => server?.args?.find((arg) => arg?.includes(`@mastra/mcp-docs-server`)) ); return hasMastraMCP; } catch (e) { console.error(e); return false; } } // src/services/service.env.ts var EnvService = class { }; // src/services/service.fileEnv.ts var FileEnvService = class extends EnvService { filePath; constructor(filePath) { super(); this.filePath = filePath; } readFile(filePath) { return new Promise((resolve, reject) => { fs3.readFile(filePath, "utf8", (err, data) => { if (err) reject(err); else resolve(data); }); }); } writeFile({ filePath, data }) { return new Promise((resolve, reject) => { fs3.writeFile(filePath, data, "utf8", (err) => { if (err) reject(err); else resolve(); }); }); } async updateEnvData({ key, value, filePath = this.filePath, data }) { const regex = new RegExp(`^${key}=.*$`, "m"); if (data.match(regex)) { data = data.replace(regex, `${key}=${value}`); } else { data += ` ${key}=${value}`; } await this.writeFile({ filePath, data }); console.log(`${key} set to ${value} in ENV file.`); return data; } async getEnvValue(key) { try { const data = await this.readFile(this.filePath); const regex = new RegExp(`^${key}=(.*)$`, "m"); const match = data.match(regex); return match?.[1] || null; } catch (err) { console.error(`Error reading ENV value: ${err}`); return null; } } async setEnvValue(key, value) { try { const data = await this.readFile(this.filePath); await this.updateEnvData({ key, value, data }); } catch (err) { console.error(`Error writing ENV value: ${err}`); } } }; // src/services/service.file.ts var FileService = class { /** * * @param inputFile the file in the starter files directory to copy * @param outputFilePath the destination path * @param replaceIfExists flag to replace if it exists * @returns */ async copyStarterFile(inputFile, outputFilePath, replaceIfExists) { const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const filePath = path.resolve(__dirname, "starter-files", inputFile); const fileString = fs3__default.readFileSync(filePath, "utf8"); if (fs3__default.existsSync(outputFilePath) && !replaceIfExists) { console.log(`${outputFilePath} already exists`); return false; } await fsExtra3.outputFile(outputFilePath, fileString); return true; } async setupEnvFile({ dbUrl }) { const envPath = path.join(process.cwd(), ".env.development"); await fsExtra3.ensureFile(envPath); const fileEnvService = new FileEnvService(envPath); await fileEnvService.setEnvValue("DB_URL", dbUrl); } getFirstExistingFile(files) { for (const f of files) { if (fs3__default.existsSync(f)) { return f; } } throw new Error("Missing required file, checked the following paths: " + files.join(", ")); } replaceValuesInFile({ filePath, replacements }) { let fileContent = fs3__default.readFileSync(filePath, "utf8"); replacements.forEach(({ search, replace }) => { fileContent = fileContent.replaceAll(search, replace); }); fs3__default.writeFileSync(filePath, fileContent); } }; var logger = createLogger({ name: "Mastra CLI", level: "debug" }); // src/commands/init/utils.ts var exec = util.promisify(child_process.exec); var getAISDKPackage = (llmProvider) => { switch (llmProvider) { case "openai": return "@ai-sdk/openai"; case "anthropic": return "@ai-sdk/anthropic"; case "groq": return "@ai-sdk/groq"; case "google": return "@ai-sdk/google"; case "cerebras": return "@ai-sdk/cerebras"; default: return "@ai-sdk/openai"; } }; var getProviderImportAndModelItem = (llmProvider) => { let providerImport = ""; let modelItem = ""; if (llmProvider === "openai") { providerImport = `import { openai } from '${getAISDKPackage(llmProvider)}';`; modelItem = `openai('gpt-4o')`; } else if (llmProvider === "anthropic") { providerImport = `import { anthropic } from '${getAISDKPackage(llmProvider)}';`; modelItem = `anthropic('claude-3-5-sonnet-20241022')`; } else if (llmProvider === "groq") { providerImport = `import { groq } from '${getAISDKPackage(llmProvider)}';`; modelItem = `groq('llama-3.3-70b-versatile')`; } else if (llmProvider === "google") { providerImport = `import { google } from '${getAISDKPackage(llmProvider)}';`; modelItem = `google('gemini-1.5-pro-latest')`; } else if (llmProvider === "cerebras") { providerImport = `import { cerebras } from '${getAISDKPackage(llmProvider)}';`; modelItem = `cerebras('llama-3.3-70b')`; } return { providerImport, modelItem }; }; async function writeAgentSample(llmProvider, destPath, addExampleTool) { const { providerImport, modelItem } = getProviderImportAndModelItem(llmProvider); const instructions = ` You are a helpful weather assistant that provides accurate weather information. Your primary function is to help users get weather details for specific locations. When responding: - Always ask for a location if none is provided - If the location name isn\u2019t in English, please translate it - If giving a location with multiple parts (e.g. "New York, NY"), use the most relevant part (e.g. "New York") - Include relevant details like humidity, wind conditions, and precipitation - Keep responses concise but informative ${addExampleTool ? "Use the weatherTool to fetch current weather data." : ""} `; const content = ` ${providerImport} import { Agent } from '@mastra/core/agent'; ${addExampleTool ? `import { weatherTool } from '../tools';` : ""} export const weatherAgent = new Agent({ name: 'Weather Agent', instructions: \`${instructions}\`, model: ${modelItem}, ${addExampleTool ? "tools: { weatherTool }," : ""} }); `; const formattedContent = await prettier.format(content, { parser: "typescript", singleQuote: true }); await fs4.writeFile(destPath, ""); await fs4.writeFile(destPath, formattedContent); } async function writeWorkflowSample(destPath, llmProvider) { const { providerImport, modelItem } = getProviderImportAndModelItem(llmProvider); const content = `${providerImport} import { Agent } from '@mastra/core/agent'; import { Step, Workflow } from '@mastra/core/workflows'; import { z } from 'zod'; const llm = ${modelItem}; const agent = new Agent({ name: 'Weather Agent', model: llm, instructions: \` You are a local activities and travel expert who excels at weather-based planning. Analyze the weather data and provide practical activity recommendations. For each day in the forecast, structure your response exactly as follows: \u{1F4C5} [Day, Month Date, Year] \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 \u{1F321}\uFE0F WEATHER SUMMARY \u2022 Conditions: [brief description] \u2022 Temperature: [X\xB0C/Y\xB0F to A\xB0C/B\xB0F] \u2022 Precipitation: [X% chance] \u{1F305} MORNING ACTIVITIES Outdoor: \u2022 [Activity Name] - [Brief description including specific location/route] Best timing: [specific time range] Note: [relevant weather consideration] \u{1F31E} AFTERNOON ACTIVITIES Outdoor: \u2022 [Activity Name] - [Brief description including specific location/route] Best timing: [specific time range] Note: [relevant weather consideration] \u{1F3E0} INDOOR ALTERNATIVES \u2022 [Activity Name] - [Brief description including specific venue] Ideal for: [weather condition that would trigger this alternative] \u26A0\uFE0F SPECIAL CONSIDERATIONS \u2022 [Any relevant weather warnings, UV index, wind conditions, etc.] Guidelines: - Suggest 2-3 time-specific outdoor activities per day - Include 1-2 indoor backup options - For precipitation >50%, lead with indoor activities - All activities must be specific to the location - Include specific venues, trails, or locations - Consider activity intensity based on temperature - Keep descriptions concise but informative Maintain this exact formatting for consistency, using the emoji and section headers as shown. \`, }); const fetchWeather = new Step({ id: 'fetch-weather', description: 'Fetches weather forecast for a given city', inputSchema: z.object({ city: z.string().describe('The city to get the weather for'), }), execute: async ({ context }) => { const triggerData = context?.getStepResult<{ city: string }>('trigger'); if (!triggerData) { throw new Error('Trigger data not found'); } const geocodingUrl = \`https://geocoding-api.open-meteo.com/v1/search?name=\${encodeURIComponent(triggerData.city)}&count=1\`; const geocodingResponse = await fetch(geocodingUrl); const geocodingData = (await geocodingResponse.json()) as { results: { latitude: number; longitude: number; name: string }[]; }; if (!geocodingData.results?.[0]) { throw new Error(\`Location '\${triggerData.city}' not found\`); } const { latitude, longitude, name } = geocodingData.results[0]; 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\`; const response = await fetch(weatherUrl); const data = (await response.json()) as { daily: { time: string[]; temperature_2m_max: number[]; temperature_2m_min: number[]; precipitation_probability_mean: number[]; weathercode: number[]; }; }; const forecast = data.daily.time.map((date: string, index: number) => ({ date, maxTemp: data.daily.temperature_2m_max[index], minTemp: data.daily.temperature_2m_min[index], precipitationChance: data.daily.precipitation_probability_mean[index], condition: getWeatherCondition(data.daily.weathercode[index]!), location: name, })); return forecast; }, }); const forecastSchema = z.array( z.object({ date: z.string(), maxTemp: z.number(), minTemp: z.number(), precipitationChance: z.number(), condition: z.string(), location: z.string(), }), ); const planActivities = new Step({ id: 'plan-activities', description: 'Suggests activities based on weather conditions', inputSchema: forecastSchema, execute: async ({ context, mastra }) => { const forecast = context?.getStepResult<z.infer<typeof forecastSchema>>('fetch-weather'); if (!forecast || forecast.length === 0) { throw new Error('Forecast data not found'); } const prompt = \`Based on the following weather forecast for \${forecast[0]?.location}, suggest appropriate activities: \${JSON.stringify(forecast, null, 2)} \`; const response = await agent.stream([ { role: 'user', content: prompt, }, ]); let activitiesText = ''; for await (const chunk of response.textStream) { process.stdout.write(chunk); activitiesText += chunk; } return { activities: activitiesText, }; }, }); function getWeatherCondition(code: number): string { const conditions: Record<number, string> = { 0: 'Clear sky', 1: 'Mainly clear', 2: 'Partly cloudy', 3: 'Overcast', 45: 'Foggy', 48: 'Depositing rime fog', 51: 'Light drizzle', 53: 'Moderate drizzle', 55: 'Dense drizzle', 61: 'Slight rain', 63: 'Moderate rain', 65: 'Heavy rain', 71: 'Slight snow fall', 73: 'Moderate snow fall', 75: 'Heavy snow fall', 95: 'Thunderstorm', }; return conditions[code] || 'Unknown'; } const weatherWorkflow = new Workflow({ name: 'weather-workflow', triggerSchema: z.object({ city: z.string().describe('The city to get the weather for'), }), }) .step(fetchWeather) .then(planActivities); weatherWorkflow.commit(); export { weatherWorkflow };`; const formattedContent = await prettier.format(content, { parser: "typescript", semi: true, singleQuote: true }); await fs4.writeFile(destPath, formattedContent); } async function writeToolSample(destPath) { const fileService = new FileService(); await fileService.copyStarterFile("tools.ts", destPath); } async function writeCodeSampleForComponents(llmprovider, component, destPath, importComponents) { switch (component) { case "agents": return writeAgentSample(llmprovider, destPath, importComponents.includes("tools")); case "tools": return writeToolSample(destPath); case "workflows": return writeWorkflowSample(destPath, llmprovider); default: return ""; } } var createComponentsDir = async (dirPath, component) => { const componentPath = dirPath + `/${component}`; await fsExtra3.ensureDir(componentPath); }; var writeIndexFile = async ({ dirPath, addAgent, addExample, addWorkflow }) => { const indexPath = dirPath + "/index.ts"; const destPath = path.join(indexPath); try { await fs4.writeFile(destPath, ""); const filteredExports = [ addWorkflow ? `workflows: { weatherWorkflow },` : "", addAgent ? `agents: { weatherAgent },` : "" ].filter(Boolean); if (!addExample) { await fs4.writeFile( destPath, ` import { Mastra } from '@mastra/core'; export const mastra = new Mastra() ` ); return; } await fs4.writeFile( destPath, ` import { Mastra } from '@mastra/core/mastra'; import { createLogger } from '@mastra/core/logger'; ${addWorkflow ? `import { weatherWorkflow } from './workflows';` : ""} ${addAgent ? `import { weatherAgent } from './agents';` : ""} export const mastra = new Mastra({ ${filteredExports.join("\n ")} logger: createLogger({ name: 'Mastra', level: 'info', }), }); ` ); } catch (err) { throw err; } }; var checkAndInstallCoreDeps = async () => { const depsService = new DepsService(); const depCheck = await depsService.checkDependencies(["@mastra/core"]); if (depCheck !== "ok") { await installCoreDeps(); } }; var spinner = yoctoSpinner({ text: "Installing Mastra core dependencies\n" }); async function installCoreDeps() { try { const confirm2 = await p.confirm({ message: "You do not have the @mastra/core package installed. Would you like to install it?", initialValue: false }); if (p.isCancel(confirm2)) { p.cancel("Installation Cancelled"); process.exit(0); } if (!confirm2) { p.cancel("Installation Cancelled"); process.exit(0); } spinner.start(); const depsService = new DepsService(); await depsService.installPackages(["@mastra/core@latest"]); spinner.success("@mastra/core installed successfully"); } catch (err) { console.error(err); } } var getAPIKey = async (provider) => { let key = "OPENAI_API_KEY"; switch (provider) { case "anthropic": key = "ANTHROPIC_API_KEY"; return key; case "groq": key = "GROQ_API_KEY"; return key; case "google": key = "GOOGLE_GENERATIVE_AI_API_KEY"; return key; case "cerebras": key = "CEREBRAS_API_KEY"; return key; default: return key; } }; var writeAPIKey = async ({ provider, apiKey = "your-api-key" }) => { const key = await getAPIKey(provider); await exec(`echo ${key}=${apiKey} >> .env.development`); }; var createMastraDir = async (directory) => { let dir = directory.trim().split("/").filter((item) => item !== ""); const dirPath = path.join(process.cwd(), ...dir, "mastra"); try { await fs4.access(dirPath); return { ok: false }; } catch { await fsExtra3.ensureDir(dirPath); return { ok: true, dirPath }; } }; var writeCodeSample = async (dirPath, component, llmProvider, importComponents) => { const destPath = dirPath + `/${component}/index.ts`; try { await writeCodeSampleForComponents(llmProvider, component, destPath, importComponents); } catch (err) { throw err; } }; var interactivePrompt = async () => { p.intro(color2.inverse("Mastra Init")); const mastraProject = await p.group( { directory: () => p.text({ message: "Where should we create the Mastra files? (default: src/)", placeholder: "src/", defaultValue: "src/" }), components: () => p.multiselect({ message: "Choose components to install:", options: [ { value: "agents", label: "Agents", hint: "recommended" }, { value: "workflows", label: "Workflows" } ] }), shouldAddTools: () => p.confirm({ message: "Add tools?", initialValue: false }), llmProvider: () => p.select({ message: "Select default provider:", options: [ { value: "openai", label: "OpenAI", hint: "recommended" }, { value: "anthropic", label: "Anthropic" }, { value: "groq", label: "Groq" }, { value: "google", label: "Google" }, { value: "cerebras", label: "Cerebras" } ] }), llmApiKey: async ({ results: { llmProvider } }) => { const keyChoice = await p.select({ message: `Enter your ${llmProvider} API key?`, options: [ { value: "skip", label: "Skip for now", hint: "default" }, { value: "enter", label: "Enter API key" } ], initialValue: "skip" }); if (keyChoice === "enter") { return p.text({ message: "Enter your API key:", placeholder: "sk-..." }); } return void 0; }, addExample: () => p.confirm({ message: "Add example", initialValue: false }), configureEditorWithDocsMCP: async () => { const windsurfIsAlreadyInstalled = await globalWindsurfMCPIsAlreadyInstalled(); const editor = await p.select({ message: `Make your AI IDE into a Mastra expert? (installs Mastra docs MCP server)`, options: [ { value: "skip", label: "Skip for now", hint: "default" }, { value: "cursor", label: "Cursor" }, { value: "windsurf", label: "Windsurf", hint: windsurfIsAlreadyInstalled ? `Already installed` : void 0 } ] }); if (editor === `skip`) return void 0; if (editor === `windsurf` && windsurfIsAlreadyInstalled) { p.log.message(` Windsurf is already installed, skipping.`); return void 0; } if (editor === `cursor`) { p.log.message( ` Note: you will need to go into Cursor Settings -> MCP Settings and manually enable the installed Mastra MCP server. ` ); } if (editor === `windsurf`) { const confirm2 = await p.select({ message: `Windsurf only supports a global MCP config (at ${windsurfGlobalMCPConfigPath}) is it ok to add/update that global config? This means the Mastra docs MCP server will be available in all your Windsurf projects.`, options: [ { value: "yes", label: "Yes, I understand" }, { value: "skip", label: "No, skip for now" } ] }); if (confirm2 !== `yes`) { return void 0; } } return editor; } }, { onCancel: () => { p.cancel("Operation cancelled."); process.exit(0); } } ); const { shouldAddTools, components, ...rest } = mastraProject; const mastraComponents = shouldAddTools ? [...components, "tools"] : components; return { ...rest, components: mastraComponents }; }; var checkPkgJson = async () => { const cwd = process.cwd(); const pkgJsonPath = path.join(cwd, "package.json"); let isPkgJsonPresent = false; try { await fsExtra3.readJSON(pkgJsonPath); isPkgJsonPresent = true; } catch { isPkgJsonPresent = false; } if (isPkgJsonPresent) { return; } logger.debug('package.json not found, create one or run "mastra create" to create a new project'); process.exit(0); }; // src/commands/init/init.ts var s = p.spinner(); var exec2 = util.promisify(child_process.exec); var init = async ({ directory, addExample = false, components, llmProvider = "openai", llmApiKey, configureEditorWithDocsMCP }) => { s.start("Initializing Mastra"); try { const result = await createMastraDir(directory); if (!result.ok) { s.stop(color2.inverse(" Mastra already initialized ")); return { success: false }; } const dirPath = result.dirPath; await Promise.all([ writeIndexFile({ dirPath, addExample, addWorkflow: components.includes("workflows"), addAgent: components.includes("agents") }), ...components.map((component) => createComponentsDir(dirPath, component)), writeAPIKey({ provider: llmProvider, apiKey: llmApiKey }) ]); if (addExample) { await Promise.all([ ...components.map( (component) => writeCodeSample(dirPath, component, llmProvider, components) ) ]); } const key = await getAPIKey(llmProvider || "openai"); const aiSdkPackage = getAISDKPackage(llmProvider); const depsService = new DepsService(); const pm = depsService.packageManager; const installCommand = getPackageManagerInstallCommand(pm); await exec2(`${pm} ${installCommand} ${aiSdkPackage}`); if (configureEditorWithDocsMCP) { await installMastraDocsMCPServer({ editor: configureEditorWithDocsMCP, directory: process.cwd() }); } s.stop(); if (!llmApiKey) { p.note(` ${color2.green("Mastra initialized successfully!")} Add your ${color2.cyan(key)} as an environment variable in your ${color2.cyan(".env.development")} file `); } else { p.note(` ${color2.green("Mastra initialized successfully!")} `); } return { success: true }; } catch (err) { s.stop(color2.inverse("An error occurred while initializing Mastra")); console.error(err); return { success: false }; } }; var exec3 = util.promisify(child_process.exec); var execWithTimeout = async (command, timeoutMs) => { try { const promise = exec3(command, { killSignal: "SIGTERM" }); if (!timeoutMs) { return await promise; } let timeoutId; const timeout = new Promise((_, reject) => { timeoutId = setTimeout(() => reject(new Error("Command timed out")), timeoutMs); }); try { const result = await Promise.race([promise, timeout]); clearTimeout(timeoutId); return result; } catch (error) { clearTimeout(timeoutId); if (error instanceof Error && error.message === "Command timed out") { throw new Error("Something went wrong during installation, please try again."); } throw error; } } catch (error) { console.error(error); throw error; } }; var createMastraProject = async ({ projectName: name, createVersionTag, timeout }) => { p.intro(color2.inverse("Mastra Create")); const projectName = name ?? await p.text({ message: "What do you want to name your project?", placeholder: "my-mastra-app", defaultValue: "my-mastra-app" }); if (p.isCancel(projectName)) { p.cancel("Operation cancelled"); process.exit(0); } const s2 = p.spinner(); s2.start("Creating project"); try { await fs4.mkdir(projectName); } catch (error) { if (error instanceof Error && "code" in error && error.code === "EEXIST") { s2.stop( `A directory named "${projectName}" already exists. Please choose a different name or delete the existing directory.` ); process.exit(1); } throw error; } process.chdir(projectName); const pm = getPackageManager(); const installCommand = getPackageManagerInstallCommand(pm); s2.message("Creating project"); await exec3(`npm init -y`); await exec3(`npm pkg set type="module"`); const depsService = new DepsService(); await depsService.addScriptsToPackageJson({ dev: "mastra dev" }); s2.stop("Project created"); s2.start(`Installing ${pm} dependencies`); await exec3(`${pm} ${installCommand} zod`); await exec3(`${pm} ${installCommand} typescript tsx @types/node --save-dev`); await exec3(`echo '{ "compilerOptions": { "target": "ES2022", "module": "ES2022", "moduleResolution": "bundler", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true, "outDir": "dist" }, "include": [ "src/**/*" ], "exclude": [ "node_modules", "dist", ".mastra" ] }' > tsconfig.json`); s2.stop(`${pm} dependencies installed`); s2.start("Installing mastra"); const versionTag = createVersionTag ? `@${createVersionTag}` : "@latest"; await execWithTimeout(`${pm} ${installCommand} mastra${versionTag}`, timeout); s2.stop("mastra installed"); s2.start("Installing @mastra/core"); await execWithTimeout(`${pm} ${installCommand} @mastra/core${versionTag}`, timeout); s2.stop("@mastra/core installed"); s2.start("Adding .gitignore"); await exec3(`echo output.txt >> .gitignore`); await exec3(`echo node_modules >> .gitignore`); await exec3(`echo dist >> .gitignore`); await exec3(`echo .mastra >> .gitignore`); await exec3(`echo .env.development >> .gitignore`); await exec3(`echo .env >> .gitignore`); await exec3(`echo *.db >> .gitignore`); s2.stop(".gitignore added"); p.outro("Project created successfully"); console.log(""); return { projectName }; }; // src/commands/create/create.ts var create = async (args2) => { const { projectName } = await createMastraProject({ projectName: args2?.projectName, createVersionTag: args2?.createVersionTag, timeout: args2?.timeout }); const directory = "/src"; if (!args2.components || !args2.llmProvider || !args2.addExample) { const result = await interactivePrompt(); await init({ ...result, llmApiKey: result?.llmApiKey }); postCreate({ projectName }); return; } const { components = [], llmProvider = "openai", addExample = false, llmApiKey } = args2; await init({ directory, components, llmProvider, addExample, llmApiKey }); postCreate({ projectName }); }; var postCreate = ({ projectName }) => { p.outro(` ${color2.green("To start your project:")} ${color2.cyan("cd")} ${projectName} ${color2.cyan("npm run dev")} `); }; export { DepsService, FileService, checkAndInstallCoreDeps, checkPkgJson, create, init, interactivePrompt, logger };