motia 0.5.11-beta.120-382150 → 0.5.11-beta.120-357906

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.
Files changed (40) hide show
  1. package/dist/cjs/cloud/build/builders/python/index.d.ts +12 -3
  2. package/dist/cjs/cloud/build/builders/python/index.js +216 -79
  3. package/dist/cjs/cloud/build/builders/python/uv-packager.d.ts +14 -0
  4. package/dist/cjs/cloud/build/builders/python/uv-packager.js +76 -0
  5. package/dist/cjs/dev.js +0 -2
  6. package/dist/cjs/utils/uv-install.d.ts +6 -0
  7. package/dist/cjs/utils/uv-install.js +129 -0
  8. package/dist/esm/cloud/build/builders/python/index.d.ts +12 -3
  9. package/dist/esm/cloud/build/builders/python/index.js +216 -79
  10. package/dist/esm/cloud/build/builders/python/uv-packager.d.ts +14 -0
  11. package/dist/esm/cloud/build/builders/python/uv-packager.js +69 -0
  12. package/dist/esm/dev.js +0 -2
  13. package/dist/esm/utils/uv-install.d.ts +6 -0
  14. package/dist/esm/utils/uv-install.js +122 -0
  15. package/dist/types/cloud/build/builders/python/index.d.ts +12 -3
  16. package/dist/types/cloud/build/builders/python/uv-packager.d.ts +14 -0
  17. package/dist/types/utils/uv-install.d.ts +6 -0
  18. package/package.json +4 -4
  19. package/dist/cjs/cloud/build/builders/python/add-package-to-archive.d.ts +0 -2
  20. package/dist/cjs/cloud/build/builders/python/add-package-to-archive.js +0 -56
  21. package/dist/cjs/cloud/build/builders/python/python-builder.py +0 -226
  22. package/dist/cjs/cloud/endpoints.d.ts +0 -2
  23. package/dist/cjs/cloud/endpoints.js +0 -107
  24. package/dist/cjs/cloud/new-deployment/listeners/streaming-deployment-listener.d.ts +0 -45
  25. package/dist/cjs/cloud/new-deployment/listeners/streaming-deployment-listener.js +0 -334
  26. package/dist/cjs/cloud/new-deployment/streams/deployment-stream.d.ts +0 -46
  27. package/dist/cjs/cloud/new-deployment/streams/deployment-stream.js +0 -111
  28. package/dist/esm/cloud/build/builders/python/add-package-to-archive.d.ts +0 -2
  29. package/dist/esm/cloud/build/builders/python/add-package-to-archive.js +0 -49
  30. package/dist/esm/cloud/build/builders/python/python-builder.py +0 -226
  31. package/dist/esm/cloud/endpoints.d.ts +0 -2
  32. package/dist/esm/cloud/endpoints.js +0 -103
  33. package/dist/esm/cloud/new-deployment/listeners/streaming-deployment-listener.d.ts +0 -45
  34. package/dist/esm/cloud/new-deployment/listeners/streaming-deployment-listener.js +0 -330
  35. package/dist/esm/cloud/new-deployment/streams/deployment-stream.d.ts +0 -46
  36. package/dist/esm/cloud/new-deployment/streams/deployment-stream.js +0 -106
  37. package/dist/types/cloud/build/builders/python/add-package-to-archive.d.ts +0 -2
  38. package/dist/types/cloud/endpoints.d.ts +0 -2
  39. package/dist/types/cloud/new-deployment/listeners/streaming-deployment-listener.d.ts +0 -45
  40. package/dist/types/cloud/new-deployment/streams/deployment-stream.d.ts +0 -46
@@ -4,9 +4,18 @@ import { BuildListener } from '../../../new-deployment/listeners/listener.types'
4
4
  export declare class PythonBuilder implements StepBuilder {
5
5
  private readonly builder;
6
6
  private readonly listener;
7
+ private uvPackager;
8
+ private uvConfig;
7
9
  constructor(builder: Builder, listener: BuildListener);
8
- private buildStep;
9
- build(step: Step): Promise<void>;
10
+ private loadUvConfig;
10
11
  buildApiSteps(steps: Step<ApiRouteConfig>[]): Promise<RouterBuildResult>;
11
- private getPythonBuilderData;
12
+ build(step: Step): Promise<void>;
13
+ private addStepToArchive;
14
+ private addPackagesToArchive;
15
+ private shouldIgnoreFile;
16
+ private normalizeStepPath;
17
+ private createRouterTemplate;
18
+ private findInternalFiles;
19
+ private resolveModulePaths;
20
+ private getModuleName;
12
21
  }
@@ -4,115 +4,252 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.PythonBuilder = void 0;
7
- const child_process_1 = require("child_process");
8
7
  const fs_1 = __importDefault(require("fs"));
9
8
  const path_1 = __importDefault(require("path"));
10
- const activate_python_env_1 = require("../../../../utils/activate-python-env");
11
9
  const archiver_1 = require("../archiver");
12
10
  const include_static_files_1 = require("../include-static-files");
13
- const add_package_to_archive_1 = require("./add-package-to-archive");
14
11
  const constants_1 = require("../../../new-deployment/constants");
12
+ const uv_packager_1 = require("./uv-packager");
15
13
  class PythonBuilder {
16
14
  constructor(builder, listener) {
17
15
  this.builder = builder;
18
16
  this.listener = listener;
19
- (0, activate_python_env_1.activatePythonVenv)({ baseDir: this.builder.projectDir });
17
+ this.uvConfig = { ...uv_packager_1.defaultUvConfig, ...this.loadUvConfig() };
18
+ this.uvPackager = new uv_packager_1.UvPackager(this.builder.projectDir, this.uvConfig);
20
19
  }
21
- async buildStep(step, archive) {
22
- const entrypointPath = step.filePath.replace(this.builder.projectDir, '');
23
- const normalizedEntrypointPath = entrypointPath.replace(/[.]step.py$/, '_step.py');
24
- const sitePackagesDir = `${process.env.PYTHON_SITE_PACKAGES}-lambda`;
25
- // Get Python builder response
26
- const { packages } = await this.getPythonBuilderData(step);
27
- // Add main file to archive
28
- if (!fs_1.default.existsSync(step.filePath)) {
29
- throw new Error(`Source file not found: ${step.filePath}`);
20
+ loadUvConfig() {
21
+ const configFiles = ['uv.config.json', '.uvrc.json'];
22
+ for (const configFile of configFiles) {
23
+ const configPath = path_1.default.join(this.builder.projectDir, configFile);
24
+ if (fs_1.default.existsSync(configPath)) {
25
+ try {
26
+ const configContent = fs_1.default.readFileSync(configPath, 'utf-8');
27
+ return JSON.parse(configContent);
28
+ }
29
+ catch (err) {
30
+ console.warn(`Warning: Failed to load UV config from ${configFile}`);
31
+ }
32
+ }
33
+ }
34
+ return {};
35
+ }
36
+ async buildApiSteps(steps) {
37
+ const zipName = 'router-python.zip';
38
+ const archive = new archiver_1.Archiver(path_1.default.join(constants_1.distDir, zipName));
39
+ if (!await this.uvPackager.checkUvInstalled()) {
40
+ throw new Error('UV is not installed. Please install UV: curl -LsSf https://astral.sh/uv/install.sh | sh');
41
+ }
42
+ const tempSitePackages = path_1.default.join(constants_1.distDir, `temp-python-packages-${Date.now()}`);
43
+ try {
44
+ await this.uvPackager.packageDependencies(tempSitePackages);
45
+ // Pequena pausa para garantir que o filesystem sincronize
46
+ await new Promise(resolve => setTimeout(resolve, 100));
47
+ await this.addPackagesToArchive(archive, tempSitePackages);
48
+ for (const step of steps) {
49
+ await this.addStepToArchive(step, archive);
50
+ }
51
+ const routerTemplate = this.createRouterTemplate(steps);
52
+ archive.append(routerTemplate, 'router.py');
53
+ (0, include_static_files_1.includeStaticFiles)(steps, this.builder, archive);
54
+ const size = await archive.finalize();
55
+ return { size, path: zipName };
56
+ }
57
+ catch (error) {
58
+ throw new Error(`Failed to build Python API router: ${error}`);
59
+ }
60
+ finally {
61
+ if (fs_1.default.existsSync(tempSitePackages)) {
62
+ fs_1.default.rmSync(tempSitePackages, { recursive: true, force: true });
63
+ }
30
64
  }
31
- archive.append(fs_1.default.createReadStream(step.filePath), path_1.default.relative(this.builder.projectDir, normalizedEntrypointPath));
32
- await Promise.all(packages.map(async (packageName) => (0, add_package_to_archive_1.addPackageToArchive)(archive, sitePackagesDir, packageName)));
33
- return normalizedEntrypointPath;
34
65
  }
35
66
  async build(step) {
36
67
  const entrypointPath = step.filePath.replace(this.builder.projectDir, '');
37
68
  const bundlePath = path_1.default.join('python', entrypointPath.replace(/(.*)\.py$/, '$1.zip'));
38
- const normalizedEntrypointPath = entrypointPath.replace(/[.]step.py$/, '_step.py');
39
69
  const outfile = path_1.default.join(constants_1.distDir, bundlePath);
70
+ this.builder.registerStep({ entrypointPath, bundlePath, step, type: 'python' });
71
+ this.listener.onBuildStart(step);
40
72
  try {
41
- // Create output directory
73
+ if (!await this.uvPackager.checkUvInstalled()) {
74
+ throw new Error('UV is not installed. Please install UV: curl -LsSf https://astral.sh/uv/install.sh | sh');
75
+ }
42
76
  fs_1.default.mkdirSync(path_1.default.dirname(outfile), { recursive: true });
43
- this.listener.onBuildStart(step);
44
- // Get Python builder response
45
- const { packages } = await this.getPythonBuilderData(step);
46
- const stepArchiver = new archiver_1.Archiver(outfile);
47
- const stepPath = await this.buildStep(step, stepArchiver);
48
- // Add main file to archive
49
- if (!fs_1.default.existsSync(step.filePath)) {
50
- throw new Error(`Source file not found: ${step.filePath}`);
77
+ const archive = new archiver_1.Archiver(outfile);
78
+ const tempSitePackages = path_1.default.join(constants_1.distDir, `temp-python-packages-${Date.now()}`);
79
+ try {
80
+ await this.uvPackager.packageDependencies(tempSitePackages);
81
+ await new Promise(resolve => setTimeout(resolve, 100));
82
+ await this.addStepToArchive(step, archive);
83
+ await this.addPackagesToArchive(archive, tempSitePackages);
84
+ (0, include_static_files_1.includeStaticFiles)([step], this.builder, archive);
85
+ const size = await archive.finalize();
86
+ this.listener.onBuildEnd(step, size);
51
87
  }
52
- stepArchiver.append(fs_1.default.createReadStream(step.filePath), path_1.default.relative(this.builder.projectDir, normalizedEntrypointPath));
53
- // Add all imported files to archive
54
- this.listener.onBuildProgress(step, 'Adding imported files to archive...');
55
- const sitePackagesDir = `${process.env.PYTHON_SITE_PACKAGES}-lambda`;
56
- (0, include_static_files_1.includeStaticFiles)([step], this.builder, stepArchiver);
57
- if (packages.length > 0) {
58
- await Promise.all(packages.map(async (packageName) => (0, add_package_to_archive_1.addPackageToArchive)(stepArchiver, sitePackagesDir, packageName)));
59
- this.listener.onBuildProgress(step, `Added ${packages.length} packages to archive`);
88
+ finally {
89
+ if (fs_1.default.existsSync(tempSitePackages)) {
90
+ fs_1.default.rmSync(tempSitePackages, { recursive: true, force: true });
91
+ }
60
92
  }
61
- // Finalize the archive and wait for completion
62
- const size = await stepArchiver.finalize();
63
- this.builder.registerStep({ entrypointPath: stepPath, bundlePath, step, type: 'python' });
64
- this.listener.onBuildEnd(step, size);
65
93
  }
66
94
  catch (err) {
67
95
  this.listener.onBuildError(step, err);
68
96
  throw err;
69
97
  }
70
98
  }
71
- async buildApiSteps(steps) {
72
- const getStepPath = (step) => {
73
- const normalizedEntrypointPath = step.filePath.replace(/[.]step.py$/, '_step.py');
74
- return normalizedEntrypointPath
75
- .replace(`${this.builder.projectDir}/`, '')
76
- .replace(/(.*)\.py$/, '$1')
77
- .replace(/\//g, '.');
78
- };
79
- const zipName = 'router-python.zip';
80
- const archive = new archiver_1.Archiver(path_1.default.join(constants_1.distDir, zipName));
81
- const dependencies = ['uvicorn', 'pydantic', 'pydantic_core', 'uvloop', 'starlette', 'typing_inspection'];
82
- const lambdaSitePackages = `${process.env.PYTHON_SITE_PACKAGES}-lambda`;
83
- await Promise.all(dependencies.map(async (packageName) => (0, add_package_to_archive_1.addPackageToArchive)(archive, lambdaSitePackages, packageName)));
84
- for (const step of steps) {
85
- await this.buildStep(step, archive);
99
+ async addStepToArchive(step, archive) {
100
+ const normalizedPath = this.normalizeStepPath(step, false);
101
+ archive.append(fs_1.default.createReadStream(step.filePath), normalizedPath);
102
+ const internalFiles = await this.findInternalFiles(step.filePath);
103
+ for (const file of internalFiles) {
104
+ const fullPath = path_1.default.join(this.builder.projectDir, file);
105
+ if (fs_1.default.existsSync(fullPath) && fullPath !== step.filePath) {
106
+ const archivePath = file.replace(/\.step\.py$/, '_step.py');
107
+ archive.append(fs_1.default.createReadStream(fullPath), archivePath);
108
+ }
109
+ }
110
+ }
111
+ async addPackagesToArchive(archive, sitePackagesDir) {
112
+ if (!fs_1.default.existsSync(sitePackagesDir)) {
113
+ console.warn(`Warning: Site packages directory not found: ${sitePackagesDir}`);
114
+ return;
115
+ }
116
+ // Verificar se o diretório está acessível
117
+ try {
118
+ fs_1.default.accessSync(sitePackagesDir, fs_1.default.constants.R_OK);
119
+ }
120
+ catch (error) {
121
+ console.warn(`Warning: Cannot access site packages directory: ${sitePackagesDir}`);
122
+ return;
86
123
  }
87
- const file = fs_1.default
124
+ const addDirectory = (dirPath, basePath = sitePackagesDir) => {
125
+ try {
126
+ const items = fs_1.default.readdirSync(dirPath);
127
+ for (const item of items) {
128
+ const fullPath = path_1.default.join(dirPath, item);
129
+ const relativePath = path_1.default.relative(basePath, fullPath);
130
+ if (this.shouldIgnoreFile(relativePath)) {
131
+ continue;
132
+ }
133
+ try {
134
+ const stat = fs_1.default.statSync(fullPath);
135
+ if (stat.isDirectory()) {
136
+ addDirectory(fullPath, basePath);
137
+ }
138
+ else {
139
+ archive.append(fs_1.default.createReadStream(fullPath), relativePath);
140
+ }
141
+ }
142
+ catch (error) {
143
+ console.warn(`Warning: Could not process file ${fullPath}: ${error}`);
144
+ }
145
+ }
146
+ }
147
+ catch (error) {
148
+ console.warn(`Warning: Could not read directory ${dirPath}: ${error}`);
149
+ }
150
+ };
151
+ addDirectory(sitePackagesDir);
152
+ }
153
+ shouldIgnoreFile(filePath) {
154
+ const ignorePatterns = [
155
+ /\.pyc$/,
156
+ /\.pyo$/,
157
+ /\.egg$/,
158
+ /\.egg-info$/,
159
+ /__pycache__/,
160
+ /\.dist-info$/,
161
+ /^tests?\//,
162
+ /^docs?\//,
163
+ /^examples?\//,
164
+ /\.pytest_cache/,
165
+ ];
166
+ return ignorePatterns.some((pattern) => pattern.test(filePath));
167
+ }
168
+ normalizeStepPath(step, normalizePythonModulePath) {
169
+ let normalizedStepPath = step.filePath
170
+ .replace(/[.]step.py$/, '_step.py') // Replace .step.py with _step.py
171
+ .replace(`${this.builder.projectDir}/`, ''); // Remove the project directory from the path
172
+ const pathParts = normalizedStepPath.split(path_1.default.sep).map((part) => part
173
+ .replace(/^[0-9]+/g, '') // Remove numeric prefixes
174
+ .replace(/[^a-zA-Z0-9._]/g, '_') // Replace any non-alphanumeric characters (except dots) with underscores
175
+ .replace(/^_/, '')); // Remove leading underscore
176
+ normalizedStepPath = normalizePythonModulePath
177
+ ? pathParts.join('.') // Convert path delimiter to dot (python module separator)
178
+ : '/' + pathParts.join(path_1.default.sep);
179
+ return normalizedStepPath;
180
+ }
181
+ createRouterTemplate(steps) {
182
+ const imports = steps
183
+ .map((step, index) => {
184
+ const moduleName = this.getModuleName(step);
185
+ return `from ${moduleName} import handler as route${index}_handler, config as route${index}_config`;
186
+ })
187
+ .join('\n');
188
+ const routerPaths = steps
189
+ .map((step, index) => {
190
+ const method = step.config.method.toUpperCase();
191
+ const path = step.config.path;
192
+ return ` '${method} ${path}': RouterPath('${step.config.name}', '${step.config.method.toLowerCase()}', route${index}_handler, route${index}_config)`;
193
+ })
194
+ .join(',\n');
195
+ return fs_1.default
88
196
  .readFileSync(path_1.default.join(__dirname, 'router_template.py'), 'utf-8')
89
- .replace('# {{imports}}', steps
90
- .map((step, index) => `from ${getStepPath(step)} import handler as route${index}_handler, config as route${index}_config`)
91
- .join('\n'))
92
- .replace('# {{router paths}}', steps
93
- .map((step, index) => `'${step.config.method} ${step.config.path}': RouterPath('${step.config.name}', '${step.config.method.toLowerCase()}', route${index}_handler, route${index}_config)`)
94
- .join(',\n '));
95
- archive.append(file, 'router.py');
96
- (0, include_static_files_1.includeStaticFiles)(steps, this.builder, archive);
97
- // Finalize the archive and wait for completion
98
- const size = await archive.finalize();
99
- return { size, path: zipName };
197
+ .replace('# {{imports}}', imports)
198
+ .replace('# {{router paths}}', routerPaths);
100
199
  }
101
- async getPythonBuilderData(step) {
102
- return new Promise((resolve, reject) => {
103
- const child = (0, child_process_1.spawn)('python', [path_1.default.join(__dirname, 'python-builder.py'), step.filePath], {
104
- cwd: this.builder.projectDir,
105
- stdio: [undefined, undefined, 'pipe', 'ipc'],
106
- });
107
- const err = [];
108
- child.on('stderr', (data) => err.push(data.toString()));
109
- child.on('message', resolve);
110
- child.on('close', (code) => {
111
- if (code !== 0) {
112
- reject(new Error(err.join('')));
200
+ async findInternalFiles(entryFile) {
201
+ const files = [];
202
+ const visited = new Set();
203
+ const analyzeFile = (filePath) => {
204
+ if (visited.has(filePath) || !fs_1.default.existsSync(filePath)) {
205
+ return;
206
+ }
207
+ visited.add(filePath);
208
+ files.push(path_1.default.relative(this.builder.projectDir, filePath));
209
+ try {
210
+ const content = fs_1.default.readFileSync(filePath, 'utf-8');
211
+ // Regex melhorada para capturar imports mais complexos
212
+ const importRegex = /^(?:from\s+([a-zA-Z_][a-zA-Z0-9_.]*)\s+import|import\s+([a-zA-Z_][a-zA-Z0-9_.]*))/gm;
213
+ let match;
214
+ while ((match = importRegex.exec(content)) !== null) {
215
+ const moduleName = match[1] || match[2]; // from X import Y ou import X
216
+ // Verificar diferentes formas de import
217
+ this.resolveModulePaths(moduleName, path_1.default.dirname(filePath)).forEach(possiblePath => {
218
+ if (fs_1.default.existsSync(possiblePath)) {
219
+ analyzeFile(possiblePath);
220
+ }
221
+ });
113
222
  }
114
- });
115
- });
223
+ }
224
+ catch (error) {
225
+ console.warn(`Could not analyze file: ${filePath}`);
226
+ }
227
+ };
228
+ analyzeFile(entryFile);
229
+ return files;
230
+ }
231
+ resolveModulePaths(moduleName, currentDir) {
232
+ const parts = moduleName.split('.');
233
+ const baseName = parts[0];
234
+ const subPath = parts.length > 1 ? path_1.default.join(...parts) : baseName;
235
+ return [
236
+ // Relativo ao arquivo atual
237
+ path_1.default.join(currentDir, `${baseName}.py`),
238
+ path_1.default.join(currentDir, baseName, '__init__.py'),
239
+ path_1.default.join(currentDir, `${subPath}.py`),
240
+ // Relativo ao diretório do projeto
241
+ path_1.default.join(this.builder.projectDir, `${baseName}.py`),
242
+ path_1.default.join(this.builder.projectDir, baseName, '__init__.py'),
243
+ path_1.default.join(this.builder.projectDir, `${subPath}.py`),
244
+ // Para utils.database -> utils/database.py
245
+ path_1.default.join(this.builder.projectDir, subPath + '.py'),
246
+ path_1.default.join(this.builder.projectDir, subPath, '__init__.py'),
247
+ ];
248
+ }
249
+ getModuleName(step) {
250
+ return this.normalizeStepPath(step, true)
251
+ .replace(/\.py$/, '')
252
+ .replace(/\//g, '.');
116
253
  }
117
254
  }
118
255
  exports.PythonBuilder = PythonBuilder;
@@ -0,0 +1,14 @@
1
+ export interface UvPackageConfig {
2
+ pythonVersion?: string;
3
+ platform?: string;
4
+ onlyBinary?: boolean;
5
+ }
6
+ export declare const defaultUvConfig: UvPackageConfig;
7
+ export declare class UvPackager {
8
+ private readonly projectDir;
9
+ private readonly config;
10
+ constructor(projectDir: string, config?: UvPackageConfig);
11
+ checkUvInstalled(): Promise<boolean>;
12
+ packageDependencies(targetDir: string): Promise<void>;
13
+ private runUvCommand;
14
+ }
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.UvPackager = exports.defaultUvConfig = void 0;
7
+ const child_process_1 = require("child_process");
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ exports.defaultUvConfig = {
11
+ pythonVersion: '3.13',
12
+ platform: 'x86_64-manylinux2014',
13
+ onlyBinary: true
14
+ };
15
+ class UvPackager {
16
+ constructor(projectDir, config = exports.defaultUvConfig) {
17
+ this.projectDir = projectDir;
18
+ this.config = config;
19
+ }
20
+ async checkUvInstalled() {
21
+ return new Promise((resolve) => {
22
+ const child = (0, child_process_1.spawn)('uv', ['--version'], { stdio: 'pipe' });
23
+ child.on('close', (code) => resolve(code === 0));
24
+ child.on('error', () => resolve(false));
25
+ });
26
+ }
27
+ async packageDependencies(targetDir) {
28
+ if (!fs_1.default.existsSync(targetDir)) {
29
+ fs_1.default.mkdirSync(targetDir, { recursive: true });
30
+ }
31
+ const requirementsFile = path_1.default.join(this.projectDir, 'requirements.txt');
32
+ if (!fs_1.default.existsSync(requirementsFile)) {
33
+ return;
34
+ }
35
+ const args = [
36
+ 'pip', 'install',
37
+ '--target', targetDir,
38
+ '--requirement', requirementsFile,
39
+ '--python-version', this.config.pythonVersion || '3.13',
40
+ '--python-platform', this.config.platform || 'x86_64-manylinux2014',
41
+ '--force-reinstall'
42
+ ];
43
+ if (this.config.onlyBinary) {
44
+ args.push('--only-binary=:all:');
45
+ }
46
+ await this.runUvCommand(args);
47
+ }
48
+ async runUvCommand(args) {
49
+ return new Promise((resolve, reject) => {
50
+ const child = (0, child_process_1.spawn)('uv', args, {
51
+ cwd: this.projectDir,
52
+ stdio: ['pipe', 'pipe', 'pipe']
53
+ });
54
+ let stdout = '';
55
+ let stderr = '';
56
+ child.stdout?.on('data', (data) => {
57
+ stdout += data.toString();
58
+ });
59
+ child.stderr?.on('data', (data) => {
60
+ stderr += data.toString();
61
+ });
62
+ child.on('close', (code) => {
63
+ if (code === 0) {
64
+ resolve(stdout);
65
+ }
66
+ else {
67
+ reject(new Error(`UV command failed: ${stderr || stdout}`));
68
+ }
69
+ });
70
+ child.on('error', (error) => {
71
+ reject(new Error(`Failed to spawn UV: ${error.message}`));
72
+ });
73
+ });
74
+ }
75
+ }
76
+ exports.UvPackager = UvPackager;
package/dist/cjs/dev.js CHANGED
@@ -11,7 +11,6 @@ const analytics_node_1 = require("@amplitude/analytics-node");
11
11
  const generate_locked_data_1 = require("./generate-locked-data");
12
12
  const dev_watchers_1 = require("./dev-watchers");
13
13
  const state_endpoints_1 = require("./dev/state-endpoints");
14
- const endpoints_1 = require("./cloud/endpoints");
15
14
  const activate_python_env_1 = require("./utils/activate-python-env");
16
15
  const analytics_1 = require("./utils/analytics");
17
16
  const version_1 = require("./version");
@@ -56,7 +55,6 @@ const dev = async (port, hostname, disableVerbose, enableMermaid) => {
56
55
  }
57
56
  watcher.init();
58
57
  (0, state_endpoints_1.stateEndpoints)(motiaServer, state);
59
- (0, endpoints_1.deployEndpoints)(motiaServer, lockedData);
60
58
  motiaServer.server.listen(port, hostname);
61
59
  console.log('🚀 Server ready and listening on port', port);
62
60
  console.log(`🔗 Open http://${hostname}:${port}/ to open workbench 🛠️`);
@@ -0,0 +1,6 @@
1
+ export interface UvInstallConfig {
2
+ baseDir: string;
3
+ isVerbose?: boolean;
4
+ pythonVersion?: string;
5
+ }
6
+ export declare const uvInstall: ({ baseDir, isVerbose, pythonVersion }: UvInstallConfig) => Promise<void>;
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.uvInstall = void 0;
7
+ const child_process_1 = require("child_process");
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const colors_1 = __importDefault(require("colors"));
11
+ const uvInstall = async ({ baseDir, isVerbose = false, pythonVersion = '3.13' }) => {
12
+ const projectDir = baseDir;
13
+ console.log('📦 Installing Python dependencies with UV...', projectDir);
14
+ try {
15
+ if (!await checkUvInstalled()) {
16
+ throw new Error('UV is not installed. Please install UV: curl -LsSf https://astral.sh/uv/install.sh | sh');
17
+ }
18
+ await ensureUvProject(projectDir, pythonVersion, isVerbose);
19
+ await installDependencies(projectDir, isVerbose);
20
+ console.log(colors_1.default.green('✅ Python dependencies installed successfully with UV'));
21
+ }
22
+ catch (error) {
23
+ const errorMessage = error instanceof Error ? error.message : String(error);
24
+ console.error('❌ UV Installation failed:', errorMessage);
25
+ throw error;
26
+ }
27
+ };
28
+ exports.uvInstall = uvInstall;
29
+ async function checkUvInstalled() {
30
+ return new Promise((resolve) => {
31
+ const child = (0, child_process_1.spawn)('uv', ['--version'], { stdio: 'pipe' });
32
+ child.on('close', (code) => {
33
+ resolve(code === 0);
34
+ });
35
+ child.on('error', () => {
36
+ resolve(false);
37
+ });
38
+ });
39
+ }
40
+ async function ensureUvProject(projectDir, pythonVersion, isVerbose) {
41
+ const pyprojectPath = path_1.default.join(projectDir, 'pyproject.toml');
42
+ if (!fs_1.default.existsSync(pyprojectPath)) {
43
+ if (isVerbose) {
44
+ console.log('📄 Creating pyproject.toml...');
45
+ }
46
+ await runUvCommand(['init', '--no-readme'], projectDir);
47
+ const pyprojectContent = generatePyprojectToml(pythonVersion);
48
+ fs_1.default.writeFileSync(pyprojectPath, pyprojectContent);
49
+ }
50
+ const pythonVersionFile = path_1.default.join(projectDir, '.python-version');
51
+ if (!fs_1.default.existsSync(pythonVersionFile)) {
52
+ fs_1.default.writeFileSync(pythonVersionFile, pythonVersion);
53
+ }
54
+ }
55
+ async function installDependencies(projectDir, isVerbose) {
56
+ const coreRequirementsPath = path_1.default.join(projectDir, 'node_modules', 'motia', 'dist', 'requirements-core.txt');
57
+ const snapRequirementsPath = path_1.default.join(projectDir, 'node_modules', 'motia', 'dist', 'requirements-snap.txt');
58
+ const localRequirements = path_1.default.join(projectDir, 'requirements.txt');
59
+ const requirementsList = [coreRequirementsPath, snapRequirementsPath, localRequirements];
60
+ for (const requirement of requirementsList) {
61
+ if (fs_1.default.existsSync(requirement)) {
62
+ if (isVerbose) {
63
+ console.log(`📄 Installing from: ${requirement}`);
64
+ }
65
+ try {
66
+ await runUvCommand(['add', '--requirements', requirement], projectDir);
67
+ }
68
+ catch (error) {
69
+ console.warn(colors_1.default.yellow(`Warning: Could not install some dependencies from ${requirement}`));
70
+ if (isVerbose) {
71
+ console.warn(error);
72
+ }
73
+ }
74
+ }
75
+ else if (isVerbose) {
76
+ console.log(colors_1.default.yellow(`⚠️ Requirements file not found: ${requirement}`));
77
+ }
78
+ }
79
+ if (isVerbose) {
80
+ console.log('🔄 Syncing UV environment...');
81
+ }
82
+ await runUvCommand(['sync'], projectDir);
83
+ }
84
+ function generatePyprojectToml(pythonVersion) {
85
+ return `[project]
86
+ name = "motia-project"
87
+ version = "0.1.0"
88
+ description = "Motia Python project"
89
+ requires-python = ">=${pythonVersion}"
90
+ dependencies = []
91
+
92
+ [build-system]
93
+ requires = ["hatchling"]
94
+ build-backend = "hatchling.build"
95
+
96
+ [tool.uv]
97
+ dev-dependencies = []
98
+ package = false
99
+
100
+ [tool.uv.sources]
101
+ `;
102
+ }
103
+ async function runUvCommand(args, cwd) {
104
+ return new Promise((resolve, reject) => {
105
+ const child = (0, child_process_1.spawn)('uv', args, {
106
+ cwd,
107
+ stdio: ['pipe', 'pipe', 'pipe']
108
+ });
109
+ let stdout = '';
110
+ let stderr = '';
111
+ child.stdout?.on('data', (data) => {
112
+ stdout += data.toString();
113
+ });
114
+ child.stderr?.on('data', (data) => {
115
+ stderr += data.toString();
116
+ });
117
+ child.on('close', (code) => {
118
+ if (code === 0) {
119
+ resolve(stdout);
120
+ }
121
+ else {
122
+ reject(new Error(`UV command failed: ${stderr || stdout}`));
123
+ }
124
+ });
125
+ child.on('error', (error) => {
126
+ reject(new Error(`Failed to spawn UV: ${error.message}`));
127
+ });
128
+ });
129
+ }
@@ -4,9 +4,18 @@ import { BuildListener } from '../../../new-deployment/listeners/listener.types'
4
4
  export declare class PythonBuilder implements StepBuilder {
5
5
  private readonly builder;
6
6
  private readonly listener;
7
+ private uvPackager;
8
+ private uvConfig;
7
9
  constructor(builder: Builder, listener: BuildListener);
8
- private buildStep;
9
- build(step: Step): Promise<void>;
10
+ private loadUvConfig;
10
11
  buildApiSteps(steps: Step<ApiRouteConfig>[]): Promise<RouterBuildResult>;
11
- private getPythonBuilderData;
12
+ build(step: Step): Promise<void>;
13
+ private addStepToArchive;
14
+ private addPackagesToArchive;
15
+ private shouldIgnoreFile;
16
+ private normalizeStepPath;
17
+ private createRouterTemplate;
18
+ private findInternalFiles;
19
+ private resolveModulePaths;
20
+ private getModuleName;
12
21
  }