@mastra/deployer 0.0.1-alpha.0
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/CHANGELOG.md +8 -0
- package/LICENSE +44 -0
- package/dist/build/bundle.d.ts +8 -0
- package/dist/build/bundle.d.ts.map +1 -0
- package/dist/build/deps.d.ts +21 -0
- package/dist/build/deps.d.ts.map +1 -0
- package/dist/build/env.d.ts +14 -0
- package/dist/build/env.d.ts.map +1 -0
- package/dist/build/fs.d.ts +22 -0
- package/dist/build/fs.d.ts.map +1 -0
- package/dist/build/index.d.ts +6 -0
- package/dist/build/index.d.ts.map +1 -0
- package/dist/build/utils.d.ts +2 -0
- package/dist/build/utils.d.ts.map +1 -0
- package/dist/deploy/base.d.ts +36 -0
- package/dist/deploy/base.d.ts.map +1 -0
- package/dist/deploy/index.d.ts +2 -0
- package/dist/deploy/index.d.ts.map +1 -0
- package/dist/deployer.cjs.development.js +2872 -0
- package/dist/deployer.cjs.development.js.map +1 -0
- package/dist/deployer.cjs.production.min.js +2 -0
- package/dist/deployer.cjs.production.min.js.map +1 -0
- package/dist/deployer.esm.js +2840 -0
- package/dist/deployer.esm.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/server/handlers/agents.d.ts +6 -0
- package/dist/server/handlers/agents.d.ts.map +1 -0
- package/dist/server/handlers/client.d.ts +7 -0
- package/dist/server/handlers/client.d.ts.map +1 -0
- package/dist/server/handlers/error.d.ts +7 -0
- package/dist/server/handlers/error.d.ts.map +1 -0
- package/dist/server/handlers/logs.d.ts +4 -0
- package/dist/server/handlers/logs.d.ts.map +1 -0
- package/dist/server/handlers/memory.d.ts +17 -0
- package/dist/server/handlers/memory.d.ts.map +1 -0
- package/dist/server/handlers/root.d.ts +3 -0
- package/dist/server/handlers/root.d.ts.map +1 -0
- package/dist/server/handlers/syncs.d.ts +3 -0
- package/dist/server/handlers/syncs.d.ts.map +1 -0
- package/dist/server/handlers/tools.d.ts +8 -0
- package/dist/server/handlers/tools.d.ts.map +1 -0
- package/dist/server/handlers/utils.d.ts +2 -0
- package/dist/server/handlers/utils.d.ts.map +1 -0
- package/dist/server/handlers/workflows.d.ts +11 -0
- package/dist/server/handlers/workflows.d.ts.map +1 -0
- package/dist/server/index.d.ts +21 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +8964 -0
- package/dist/server/types.d.ts +5 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/server/welcome.d.ts +2 -0
- package/dist/server/welcome.d.ts.map +1 -0
- package/package.json +61 -0
- package/src/build/bundle.ts +224 -0
- package/src/build/deps.ts +128 -0
- package/src/build/env.ts +76 -0
- package/src/build/fs.ts +66 -0
- package/src/build/index.ts +5 -0
- package/src/build/utils.ts +12 -0
- package/src/deploy/base.ts +171 -0
- package/src/deploy/index.ts +1 -0
- package/src/index.ts +3 -0
- package/src/server/handlers/agents.ts +121 -0
- package/src/server/handlers/client.ts +36 -0
- package/src/server/handlers/error.ts +29 -0
- package/src/server/handlers/logs.ts +24 -0
- package/src/server/handlers/memory.ts +208 -0
- package/src/server/handlers/root.ts +6 -0
- package/src/server/handlers/syncs.ts +19 -0
- package/src/server/handlers/tools.ts +156 -0
- package/src/server/handlers/utils.ts +15 -0
- package/src/server/handlers/workflows.ts +60 -0
- package/src/server/index.ts +185 -0
- package/src/server/types.ts +4 -0
- package/src/server/welcome.ts +105 -0
- package/tsconfig.json +10 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/server/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAS,SAAQ,KAAK;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const html = "\n<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Welcome to Mastra</title>\n <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/inter-ui/3.19.3/inter.min.css\" />\n <style>\n body {\n margin: 0;\n padding: 0;\n background-color: #0d0d0d;\n color: #ffffff;\n font-family:\n 'Inter',\n -apple-system,\n BlinkMacSystemFont,\n system-ui,\n sans-serif;\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n }\n\n main {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 2rem;\n text-align: center;\n }\n\n h1 {\n font-size: 4rem;\n font-weight: 600;\n margin: 0 0 1rem 0;\n background: linear-gradient(to right, #fff, #ccc);\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent;\n line-height: 1.2;\n }\n\n .subtitle {\n color: #9ca3af;\n font-size: 1.25rem;\n max-width: 600px;\n margin: 0 auto 3rem auto;\n line-height: 1.6;\n }\n\n .docs-link {\n background-color: #1a1a1a;\n padding: 1rem 2rem;\n border-radius: 0.5rem;\n display: flex;\n align-items: center;\n gap: 1rem;\n font-family: monospace;\n font-size: 1rem;\n color: #ffffff;\n text-decoration: none;\n transition: background-color 0.2s;\n }\n\n .docs-link:hover {\n background-color: #252525;\n }\n\n .arrow-icon {\n transition: transform 0.2s;\n }\n\n .docs-link:hover .arrow-icon {\n transform: translateX(4px);\n }\n </style>\n </head>\n <body>\n <main>\n <h1>Welcome to Mastra</h1>\n <p class=\"subtitle\">\n From the team that brought you Gatsby: prototype and productionize AI features with a modern JS/TS stack.\n </p>\n\n <a href=\"https://mastra.ai/docs\" class=\"docs-link\">\n Browse the docs\n <svg\n class=\"arrow-icon\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n >\n <path d=\"M5 12h14M12 5l7 7-7 7\" />\n </svg>\n </a>\n </main>\n </body>\n</html>\n";
|
|
2
|
+
//# sourceMappingURL=welcome.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"welcome.d.ts","sourceRoot":"","sources":["../../src/server/welcome.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,IAAI,ijFAwGhB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mastra/deployer",
|
|
3
|
+
"version": "0.0.1-alpha.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"module": "dist/deployer.esm.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/deployer.esm.js"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"default": "./dist/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"./server": {
|
|
21
|
+
"import": "./dist/server/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./package.json": "./package.json"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [],
|
|
26
|
+
"author": "",
|
|
27
|
+
"license": "ISC",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@hono/node-server": "^1.13.7",
|
|
30
|
+
"date-fns": "^4.1.0",
|
|
31
|
+
"dotenv": "^16.3.1",
|
|
32
|
+
"esbuild": "^0.24.2",
|
|
33
|
+
"execa": "^9.3.1",
|
|
34
|
+
"fs-extra": "^11.2.0",
|
|
35
|
+
"hono": "^4.6.17",
|
|
36
|
+
"superjson": "^2.2.2",
|
|
37
|
+
"type-fest": "^4.30.0",
|
|
38
|
+
"wrangler": "^3.103.2",
|
|
39
|
+
"zod": "^3.24.1",
|
|
40
|
+
"zod-to-json-schema": "^3.24.1",
|
|
41
|
+
"@mastra/core": "0.1.27-alpha.66"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@babel/preset-env": "^7.26.0",
|
|
45
|
+
"@babel/preset-typescript": "^7.26.0",
|
|
46
|
+
"@tsconfig/recommended": "^1.0.7",
|
|
47
|
+
"@types/fs-extra": "^11.0.4",
|
|
48
|
+
"@types/jsdom": "^21.1.7",
|
|
49
|
+
"@types/node": "^22.9.0",
|
|
50
|
+
"@types/pg": "^8.11.10",
|
|
51
|
+
"concurrently": "^9.1.0",
|
|
52
|
+
"dts-cli": "^2.0.5",
|
|
53
|
+
"vitest": "^2.1.8"
|
|
54
|
+
},
|
|
55
|
+
"scripts": {
|
|
56
|
+
"prebuild:app": "esbuild --platform=node --target=node20 --format=esm --bundle --outdir=dist/server src/server/index.ts",
|
|
57
|
+
"build": "dts build && pnpm prebuild:app",
|
|
58
|
+
"build:dev": "concurrently 'dts watch' 'sleep 5 && pnpm prebuild:app --watch'",
|
|
59
|
+
"test": "vitest run"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { Workflow, Step } from '@mastra/core';
|
|
2
|
+
import * as esbuild from 'esbuild';
|
|
3
|
+
import { type BuildOptions } from 'esbuild';
|
|
4
|
+
import { writeFileSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
|
|
8
|
+
import { FileService } from './fs.js';
|
|
9
|
+
import { upsertMastraDir } from './utils.js';
|
|
10
|
+
|
|
11
|
+
const buildWorkflow = new Workflow({
|
|
12
|
+
name: 'Build',
|
|
13
|
+
triggerSchema: z.object({
|
|
14
|
+
buildName: z.string(),
|
|
15
|
+
entry: z.string(),
|
|
16
|
+
outfile: z.string().optional(),
|
|
17
|
+
useBanner: z.boolean().optional(),
|
|
18
|
+
devMode: z.boolean().optional(),
|
|
19
|
+
}),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
buildWorkflow.__setLogger(null as any);
|
|
23
|
+
|
|
24
|
+
const ensureDir = new Step({
|
|
25
|
+
id: 'Ensure Directory',
|
|
26
|
+
execute: async () => {
|
|
27
|
+
// Ensure .mastra directory exists
|
|
28
|
+
upsertMastraDir();
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const bundleStep = new Step({
|
|
33
|
+
id: 'Bundle',
|
|
34
|
+
execute: async ({ context }) => {
|
|
35
|
+
const devMode = context.machineContext?.triggerData.devMode;
|
|
36
|
+
const buildName = context.machineContext?.triggerData.buildName;
|
|
37
|
+
const useBanner = context.machineContext?.triggerData.useBanner;
|
|
38
|
+
|
|
39
|
+
const entry = context.machineContext?.triggerData.entry;
|
|
40
|
+
|
|
41
|
+
const outfile = context.machineContext?.triggerData.outfile;
|
|
42
|
+
|
|
43
|
+
const fileService = new FileService();
|
|
44
|
+
const entryPoint = fileService.getFirstExistingFile([entry]);
|
|
45
|
+
const outfilePath = outfile || join(process.cwd(), '.mastra', 'mastra.mjs');
|
|
46
|
+
|
|
47
|
+
const plugins: any[] = [];
|
|
48
|
+
|
|
49
|
+
let missingMastraDependency = false;
|
|
50
|
+
|
|
51
|
+
const externalIfMissingPlugin = {
|
|
52
|
+
name: 'external-if-missing',
|
|
53
|
+
setup(build: any) {
|
|
54
|
+
build.onResolve(
|
|
55
|
+
{ filter: /^.*$/ },
|
|
56
|
+
// @ts-ignore
|
|
57
|
+
(args: any) => {
|
|
58
|
+
if (!args.importer.endsWith('.mastra/index.mjs')) return;
|
|
59
|
+
try {
|
|
60
|
+
const resolvedPath = import.meta.resolve(args.path);
|
|
61
|
+
if (!resolvedPath || resolvedPath.includes('_npx/')) {
|
|
62
|
+
missingMastraDependency = true;
|
|
63
|
+
return { path: args.path, external: true };
|
|
64
|
+
}
|
|
65
|
+
} catch (e) {
|
|
66
|
+
missingMastraDependency = true;
|
|
67
|
+
return { path: args.path, external: true };
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
);
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
if (devMode) {
|
|
75
|
+
plugins.push(externalIfMissingPlugin);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const esbuildConfig: BuildOptions = {
|
|
79
|
+
entryPoints: [entryPoint],
|
|
80
|
+
bundle: true,
|
|
81
|
+
platform: 'node',
|
|
82
|
+
format: 'esm',
|
|
83
|
+
outfile: outfilePath,
|
|
84
|
+
target: 'node20',
|
|
85
|
+
sourcemap: true,
|
|
86
|
+
logLevel: 'silent',
|
|
87
|
+
minify: false,
|
|
88
|
+
metafile: true,
|
|
89
|
+
mainFields: ['module', 'main'],
|
|
90
|
+
conditions: ['import', 'node'],
|
|
91
|
+
logOverride: {
|
|
92
|
+
'commonjs-variable-in-esm': 'silent',
|
|
93
|
+
},
|
|
94
|
+
external: [
|
|
95
|
+
'execa',
|
|
96
|
+
// Mark node built-ins as external
|
|
97
|
+
'assert',
|
|
98
|
+
'buffer',
|
|
99
|
+
'child_process',
|
|
100
|
+
'cluster',
|
|
101
|
+
'constants',
|
|
102
|
+
'crypto',
|
|
103
|
+
'dgram',
|
|
104
|
+
'dns',
|
|
105
|
+
'events',
|
|
106
|
+
'fs',
|
|
107
|
+
'http',
|
|
108
|
+
'http2',
|
|
109
|
+
'https',
|
|
110
|
+
'module',
|
|
111
|
+
'net',
|
|
112
|
+
'os',
|
|
113
|
+
'path',
|
|
114
|
+
'punycode',
|
|
115
|
+
'querystring',
|
|
116
|
+
'readline',
|
|
117
|
+
'repl',
|
|
118
|
+
'stream',
|
|
119
|
+
'string_decoder',
|
|
120
|
+
'sys',
|
|
121
|
+
'timers',
|
|
122
|
+
'tls',
|
|
123
|
+
'tty',
|
|
124
|
+
'url',
|
|
125
|
+
'util',
|
|
126
|
+
'v8',
|
|
127
|
+
'vm',
|
|
128
|
+
'wasi',
|
|
129
|
+
'worker_threads',
|
|
130
|
+
'zlib',
|
|
131
|
+
'chromium-bidi/lib/cjs/bidiMapper/BidiMapper',
|
|
132
|
+
'chromium-bidi/lib/cjs/cdp/CdpConnection',
|
|
133
|
+
|
|
134
|
+
// Your packages
|
|
135
|
+
'@mastra/core',
|
|
136
|
+
'@mastra/memory',
|
|
137
|
+
'@mastra/engine',
|
|
138
|
+
'@mastra/rag',
|
|
139
|
+
'@mastra/evals',
|
|
140
|
+
'@mastra/mcp',
|
|
141
|
+
'@mastra/tts',
|
|
142
|
+
'@mastra/firecrawl',
|
|
143
|
+
'@mastra/github',
|
|
144
|
+
'@mastra/stabilityai',
|
|
145
|
+
'@mastra/deployer',
|
|
146
|
+
'@mastra/deployer-cloudflare',
|
|
147
|
+
'@mastra/deployer-netlify',
|
|
148
|
+
'@mastra/deployer-vercel',
|
|
149
|
+
],
|
|
150
|
+
plugins,
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
if (useBanner) {
|
|
154
|
+
esbuildConfig.banner = {
|
|
155
|
+
js: `
|
|
156
|
+
import { createRequire } from "node:module";
|
|
157
|
+
const require = createRequire(import.meta.url || 'file:///');
|
|
158
|
+
`,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const result = await esbuild.build(esbuildConfig);
|
|
163
|
+
|
|
164
|
+
if (devMode && missingMastraDependency) {
|
|
165
|
+
console.error(
|
|
166
|
+
`Missing Mastra dependency. Please install the mastra package in your project or globally using npm i -g mastra`,
|
|
167
|
+
);
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Log build results
|
|
172
|
+
console.log(`[${buildName}]: build completed successfully`);
|
|
173
|
+
|
|
174
|
+
return result;
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const analyzeStep = new Step({
|
|
179
|
+
id: 'Analyze',
|
|
180
|
+
execute: async ({ context }) => {
|
|
181
|
+
if (context?.machineContext?.stepResults?.Bundle?.status !== 'success') {
|
|
182
|
+
throw new Error('Bundle step failed');
|
|
183
|
+
}
|
|
184
|
+
writeFileSync(
|
|
185
|
+
join(process.cwd(), '.mastra', 'metafile.json'),
|
|
186
|
+
JSON.stringify(context.machineContext?.stepResults.Bundle.payload.metafile, null, 2),
|
|
187
|
+
);
|
|
188
|
+
const res = await esbuild.analyzeMetafile(context.machineContext?.stepResults.Bundle.payload.metafile);
|
|
189
|
+
return res;
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
buildWorkflow.step(ensureDir).then(bundleStep).then(analyzeStep).commit();
|
|
194
|
+
|
|
195
|
+
export async function bundle(
|
|
196
|
+
entry: string,
|
|
197
|
+
{
|
|
198
|
+
useBanner = true,
|
|
199
|
+
buildName = 'Build',
|
|
200
|
+
outfile,
|
|
201
|
+
devMode,
|
|
202
|
+
}: { devMode?: boolean; outfile?: string; entryFile?: string; buildName?: string; useBanner?: boolean } = {},
|
|
203
|
+
) {
|
|
204
|
+
const { start } = buildWorkflow.createRun();
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
await start({
|
|
208
|
+
triggerData: {
|
|
209
|
+
buildName,
|
|
210
|
+
entry,
|
|
211
|
+
useBanner,
|
|
212
|
+
outfile,
|
|
213
|
+
devMode,
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// console.log(JSON.stringify(result, null, 2));
|
|
218
|
+
|
|
219
|
+
// return result;
|
|
220
|
+
} catch (error) {
|
|
221
|
+
console.error('Failed to build:', error);
|
|
222
|
+
process.exit(1);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { execa } from 'execa';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path, { dirname } from 'path';
|
|
4
|
+
import { PackageJson } from 'type-fest';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
|
|
7
|
+
import fsExtra from 'fs-extra/esm';
|
|
8
|
+
import fsPromises from 'fs/promises';
|
|
9
|
+
|
|
10
|
+
export class Deps {
|
|
11
|
+
private packageManager: string;
|
|
12
|
+
|
|
13
|
+
constructor() {
|
|
14
|
+
this.packageManager = this.getPackageManager();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
private findLockFile(dir: string): string | null {
|
|
18
|
+
const lockFiles = ['pnpm-lock.yaml', 'package-lock.json', 'yarn.lock'];
|
|
19
|
+
for (const file of lockFiles) {
|
|
20
|
+
if (fs.existsSync(path.join(dir, file))) {
|
|
21
|
+
return file;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const parentDir = path.resolve(dir, '..');
|
|
25
|
+
if (parentDir !== dir) {
|
|
26
|
+
return this.findLockFile(parentDir);
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private getPackageManager(): string {
|
|
32
|
+
const lockFile = this.findLockFile(process.cwd());
|
|
33
|
+
switch (lockFile) {
|
|
34
|
+
case 'pnpm-lock.yaml':
|
|
35
|
+
return 'pnpm';
|
|
36
|
+
case 'package-lock.json':
|
|
37
|
+
return 'npm';
|
|
38
|
+
case 'yarn.lock':
|
|
39
|
+
return 'yarn';
|
|
40
|
+
default:
|
|
41
|
+
return 'npm';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public async install(packages: string[] = []) {
|
|
46
|
+
let runCommand = this.packageManager;
|
|
47
|
+
if (this.packageManager === 'npm') {
|
|
48
|
+
runCommand = `${this.packageManager} i`;
|
|
49
|
+
} else {
|
|
50
|
+
runCommand = `${this.packageManager} ${packages?.length > 0 ? `add` : `install`}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return execa(`${runCommand}`, packages, {
|
|
54
|
+
all: true,
|
|
55
|
+
shell: true,
|
|
56
|
+
stdio: 'inherit',
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public async installPackages(packages: string[]) {
|
|
61
|
+
let runCommand = this.packageManager;
|
|
62
|
+
if (this.packageManager === 'npm') {
|
|
63
|
+
runCommand = `${this.packageManager} i`;
|
|
64
|
+
} else {
|
|
65
|
+
runCommand = `${this.packageManager} add`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const packageList = packages.join(' ');
|
|
69
|
+
return execa(`${runCommand} ${packageList}`, {
|
|
70
|
+
all: true,
|
|
71
|
+
shell: true,
|
|
72
|
+
stdio: 'inherit',
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public async checkDependencies(dependencies: string[]): Promise<string> {
|
|
77
|
+
try {
|
|
78
|
+
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
await fsPromises.access(packageJsonPath);
|
|
82
|
+
} catch {
|
|
83
|
+
return 'No package.json file found in the current directory';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const packageJson = JSON.parse(await fsPromises.readFile(packageJsonPath, 'utf-8'));
|
|
87
|
+
for (const dependency of dependencies) {
|
|
88
|
+
if (!packageJson.dependencies || !packageJson.dependencies[dependency]) {
|
|
89
|
+
return `Please install ${dependency} before running this command (${this.packageManager} install ${dependency})`;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return 'ok';
|
|
94
|
+
} catch (err) {
|
|
95
|
+
console.error(err);
|
|
96
|
+
return 'Could not check dependencies';
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
public async getProjectName() {
|
|
101
|
+
try {
|
|
102
|
+
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
103
|
+
const packageJson = await fsPromises.readFile(packageJsonPath, 'utf-8');
|
|
104
|
+
const pkg = JSON.parse(packageJson);
|
|
105
|
+
return pkg.name;
|
|
106
|
+
} catch (err) {
|
|
107
|
+
throw err;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
public async getPackageVersion() {
|
|
112
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
113
|
+
const __dirname = dirname(__filename);
|
|
114
|
+
const pkgJsonPath = path.join(__dirname, '..', '..', 'package.json');
|
|
115
|
+
|
|
116
|
+
const content = (await fsExtra.readJSON(pkgJsonPath)) as PackageJson;
|
|
117
|
+
return content.version;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public async addScriptsToPackageJson(scripts: Record<string, string>) {
|
|
121
|
+
const packageJson = JSON.parse(await fsPromises.readFile('package.json', 'utf-8'));
|
|
122
|
+
packageJson.scripts = {
|
|
123
|
+
...packageJson.scripts,
|
|
124
|
+
...scripts,
|
|
125
|
+
};
|
|
126
|
+
await fsPromises.writeFile('package.json', JSON.stringify(packageJson, null, 2));
|
|
127
|
+
}
|
|
128
|
+
}
|
package/src/build/env.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
|
|
3
|
+
export abstract class EnvService {
|
|
4
|
+
abstract getEnvValue(key: string): Promise<string | null>;
|
|
5
|
+
abstract setEnvValue(key: string, value: string): Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class FileEnvService extends EnvService {
|
|
9
|
+
private filePath: string;
|
|
10
|
+
|
|
11
|
+
constructor(filePath: string) {
|
|
12
|
+
super();
|
|
13
|
+
this.filePath = filePath;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
private readFile(filePath: string): Promise<string> {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
fs.readFile(filePath, 'utf8', (err: NodeJS.ErrnoException | null, data: string) => {
|
|
19
|
+
if (err) reject(err);
|
|
20
|
+
else resolve(data);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private writeFile({ filePath, data }: { filePath: string; data: string }): Promise<void> {
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
fs.writeFile(filePath, data, 'utf8', (err: NodeJS.ErrnoException | null) => {
|
|
28
|
+
if (err) reject(err);
|
|
29
|
+
else resolve();
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private async updateEnvData({
|
|
35
|
+
key,
|
|
36
|
+
value,
|
|
37
|
+
filePath = this.filePath,
|
|
38
|
+
data,
|
|
39
|
+
}: {
|
|
40
|
+
key: string;
|
|
41
|
+
value: string;
|
|
42
|
+
filePath?: string;
|
|
43
|
+
data: string;
|
|
44
|
+
}): Promise<string> {
|
|
45
|
+
const regex = new RegExp(`^${key}=.*$`, 'm');
|
|
46
|
+
if (data.match(regex)) {
|
|
47
|
+
data = data.replace(regex, `${key}=${value}`);
|
|
48
|
+
} else {
|
|
49
|
+
data += `\n${key}=${value}`;
|
|
50
|
+
}
|
|
51
|
+
await this.writeFile({ filePath, data });
|
|
52
|
+
console.log(`${key} set to ${value} in ENV file.`);
|
|
53
|
+
return data;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async getEnvValue(key: string): Promise<string | null> {
|
|
57
|
+
try {
|
|
58
|
+
const data = await this.readFile(this.filePath);
|
|
59
|
+
const regex = new RegExp(`^${key}=(.*)$`, 'm');
|
|
60
|
+
const match = data.match(regex);
|
|
61
|
+
return match?.[1] || null;
|
|
62
|
+
} catch (err) {
|
|
63
|
+
console.error(`Error reading ENV value: ${err}`);
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async setEnvValue(key: string, value: string): Promise<void> {
|
|
69
|
+
try {
|
|
70
|
+
const data = await this.readFile(this.filePath);
|
|
71
|
+
await this.updateEnvData({ key, value, data });
|
|
72
|
+
} catch (err) {
|
|
73
|
+
console.error(`Error writing ENV value: ${err}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
package/src/build/fs.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
import fsExtra from 'fs-extra/esm';
|
|
6
|
+
|
|
7
|
+
import { FileEnvService } from './env.js';
|
|
8
|
+
|
|
9
|
+
export class FileService {
|
|
10
|
+
/**
|
|
11
|
+
*
|
|
12
|
+
* @param inputFile the file in the starter files directory to copy
|
|
13
|
+
* @param outputFilePath the destination path
|
|
14
|
+
* @param replaceIfExists flag to replace if it exists
|
|
15
|
+
* @returns
|
|
16
|
+
*/
|
|
17
|
+
public async copyStarterFile(inputFile: string, outputFilePath: string, replaceIfExists?: boolean) {
|
|
18
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
19
|
+
const __dirname = path.dirname(__filename);
|
|
20
|
+
const filePath = path.resolve(__dirname, '..', 'starter-files', inputFile);
|
|
21
|
+
const fileString = fs.readFileSync(filePath, 'utf8');
|
|
22
|
+
|
|
23
|
+
if (fs.existsSync(outputFilePath) && !replaceIfExists) {
|
|
24
|
+
console.log(`${outputFilePath} already exists`);
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
await fsExtra.outputFile(outputFilePath, fileString);
|
|
29
|
+
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public async setupEnvFile({ dbUrl }: { dbUrl: string }) {
|
|
34
|
+
const envPath = path.join(process.cwd(), '.env.development');
|
|
35
|
+
|
|
36
|
+
await fsExtra.ensureFile(envPath);
|
|
37
|
+
|
|
38
|
+
const fileEnvService = new FileEnvService(envPath);
|
|
39
|
+
await fileEnvService.setEnvValue('DB_URL', dbUrl);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public getFirstExistingFile(files: string[]): string {
|
|
43
|
+
for (const f of files) {
|
|
44
|
+
if (fs.existsSync(f)) {
|
|
45
|
+
return f;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
throw new Error('Missing required file, checked the following paths: ' + files.join(', '));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public replaceValuesInFile({
|
|
53
|
+
filePath,
|
|
54
|
+
replacements,
|
|
55
|
+
}: {
|
|
56
|
+
filePath: string;
|
|
57
|
+
replacements: { search: string; replace: string }[];
|
|
58
|
+
}) {
|
|
59
|
+
let fileContent = fs.readFileSync(filePath, 'utf8');
|
|
60
|
+
replacements.forEach(({ search, replace }) => {
|
|
61
|
+
fileContent = fileContent.replaceAll(search, replace);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
fs.writeFileSync(filePath, fileContent);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import { existsSync, mkdirSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
|
|
5
|
+
export function upsertMastraDir() {
|
|
6
|
+
const dirPath = join(process.cwd(), '.mastra');
|
|
7
|
+
|
|
8
|
+
if (!existsSync(dirPath)) {
|
|
9
|
+
mkdirSync(dirPath, { recursive: true });
|
|
10
|
+
execSync(`echo ".mastra" >> .gitignore`);
|
|
11
|
+
}
|
|
12
|
+
}
|