motia 0.5.11-beta.120-391598 → 0.5.11-beta.120-207545
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/cjs/cloud/build/builders/python/index.d.ts +11 -3
- package/dist/cjs/cloud/build/builders/python/index.js +215 -79
- package/dist/cjs/cloud/build/builders/python/uv-packager.d.ts +13 -0
- package/dist/cjs/cloud/build/builders/python/uv-packager.js +77 -0
- package/dist/cjs/cloud/cli/deploy.js +8 -4
- package/dist/cjs/cloud/new-deployment/cloud-api/create-deployment.d.ts +3 -1
- package/dist/cjs/cloud/new-deployment/cloud-api/index.d.ts +3 -1
- package/dist/cjs/create/templates/python/steps/state_audit_cron_step.py.txt +1 -1
- package/dist/cjs/create/templates/typescript/steps/state-audit-cron.step.ts.txt +1 -1
- package/dist/cjs/cursor-rules/index.js +32 -3
- package/dist/cjs/install.js +5 -0
- package/dist/cjs/utils/ensure-uv.d.ts +1 -0
- package/dist/cjs/utils/ensure-uv.js +81 -0
- package/dist/esm/cloud/build/builders/python/index.d.ts +11 -3
- package/dist/esm/cloud/build/builders/python/index.js +215 -79
- package/dist/esm/cloud/build/builders/python/uv-packager.d.ts +13 -0
- package/dist/esm/cloud/build/builders/python/uv-packager.js +70 -0
- package/dist/esm/cloud/cli/deploy.js +8 -4
- package/dist/esm/cloud/new-deployment/cloud-api/create-deployment.d.ts +3 -1
- package/dist/esm/cloud/new-deployment/cloud-api/index.d.ts +3 -1
- package/dist/esm/create/templates/python/steps/state_audit_cron_step.py.txt +1 -1
- package/dist/esm/create/templates/typescript/steps/state-audit-cron.step.ts.txt +1 -1
- package/dist/esm/cursor-rules/index.js +32 -3
- package/dist/esm/install.js +5 -0
- package/dist/esm/utils/ensure-uv.d.ts +1 -0
- package/dist/esm/utils/ensure-uv.js +77 -0
- package/dist/types/cloud/build/builders/python/index.d.ts +11 -3
- package/dist/types/cloud/build/builders/python/uv-packager.d.ts +13 -0
- package/dist/types/cloud/new-deployment/cloud-api/create-deployment.d.ts +3 -1
- package/dist/types/cloud/new-deployment/cloud-api/index.d.ts +3 -1
- package/dist/types/utils/ensure-uv.d.ts +1 -0
- package/package.json +4 -4
- package/dist/cjs/cloud/build/builders/python/add-package-to-archive.d.ts +0 -2
- package/dist/cjs/cloud/build/builders/python/add-package-to-archive.js +0 -56
- package/dist/cjs/cloud/build/builders/python/python-builder.py +0 -226
- package/dist/dot-files/.cursor/rules/ai-agent-patterns.mdc +0 -725
- package/dist/dot-files/.cursor/rules/api-design-patterns.mdc +0 -740
- package/dist/dot-files/.cursor/rules/api-steps.mdc +0 -230
- package/dist/dot-files/.cursor/rules/architecture.mdc +0 -189
- package/dist/dot-files/.cursor/rules/authentication-patterns.mdc +0 -620
- package/dist/dot-files/.cursor/rules/background-job-patterns.mdc +0 -628
- package/dist/dot-files/.cursor/rules/complete-application-patterns.mdc +0 -433
- package/dist/dot-files/.cursor/rules/complete-backend-generator.mdc +0 -415
- package/dist/dot-files/.cursor/rules/cron-steps.mdc +0 -257
- package/dist/dot-files/.cursor/rules/event-steps.mdc +0 -504
- package/dist/dot-files/.cursor/rules/instructions.mdc +0 -15
- package/dist/dot-files/.cursor/rules/multi-language-workflows.mdc +0 -1059
- package/dist/dot-files/.cursor/rules/noop-steps.mdc +0 -57
- package/dist/dot-files/.cursor/rules/production-deployment.mdc +0 -668
- package/dist/dot-files/.cursor/rules/realtime-streaming.mdc +0 -656
- package/dist/dot-files/.cursor/rules/state-management.mdc +0 -371
- package/dist/dot-files/.cursor/rules/steps.mdc +0 -373
- package/dist/dot-files/.cursor/rules/testing.mdc +0 -329
- package/dist/dot-files/.cursor/rules/typescript.mdc +0 -409
- package/dist/dot-files/.cursor/rules/ui-steps.mdc +0 -90
- package/dist/dot-files/.cursor/rules/workflow-patterns.mdc +0 -938
- package/dist/dot-files/AGENTS.md +0 -397
- package/dist/dot-files/CLAUDE.md +0 -1032
- package/dist/dot-files/README.md +0 -58
- package/dist/esm/cloud/build/builders/python/add-package-to-archive.d.ts +0 -2
- package/dist/esm/cloud/build/builders/python/add-package-to-archive.js +0 -49
- package/dist/esm/cloud/build/builders/python/python-builder.py +0 -226
- package/dist/types/cloud/build/builders/python/add-package-to-archive.d.ts +0 -2
|
@@ -4,9 +4,17 @@ 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;
|
|
7
8
|
constructor(builder: Builder, listener: BuildListener);
|
|
8
|
-
private buildStep;
|
|
9
|
-
build(step: Step): Promise<void>;
|
|
10
9
|
buildApiSteps(steps: Step<ApiRouteConfig>[]): Promise<RouterBuildResult>;
|
|
11
|
-
|
|
10
|
+
build(step: Step): Promise<void>;
|
|
11
|
+
private addStepToArchive;
|
|
12
|
+
private addPackagesToArchive;
|
|
13
|
+
private shouldIgnoreFile;
|
|
14
|
+
private normalizeStepPath;
|
|
15
|
+
private createRouterTemplate;
|
|
16
|
+
private findInternalFiles;
|
|
17
|
+
private resolveModulePaths;
|
|
18
|
+
private getModuleName;
|
|
19
|
+
private waitForDirectoryReady;
|
|
12
20
|
}
|
|
@@ -4,116 +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");
|
|
13
|
+
const activate_python_env_1 = require("../../../../utils/activate-python-env");
|
|
15
14
|
class PythonBuilder {
|
|
16
15
|
constructor(builder, listener) {
|
|
17
16
|
this.builder = builder;
|
|
18
17
|
this.listener = listener;
|
|
19
18
|
(0, activate_python_env_1.activatePythonVenv)({ baseDir: this.builder.projectDir });
|
|
19
|
+
this.uvPackager = new uv_packager_1.UvPackager(this.builder.projectDir);
|
|
20
20
|
}
|
|
21
|
-
async
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
async buildApiSteps(steps) {
|
|
22
|
+
const zipName = 'router-python.zip';
|
|
23
|
+
const archive = new archiver_1.Archiver(path_1.default.join(constants_1.distDir, zipName));
|
|
24
|
+
const tempSitePackages = path_1.default.join(constants_1.distDir, `temp-python-packages-${Date.now()}`);
|
|
25
|
+
try {
|
|
26
|
+
await this.uvPackager.packageDependencies(tempSitePackages);
|
|
27
|
+
// Wait for directory to be ready with proper access checks
|
|
28
|
+
await this.waitForDirectoryReady(tempSitePackages);
|
|
29
|
+
await this.addPackagesToArchive(archive, tempSitePackages);
|
|
30
|
+
for (const step of steps) {
|
|
31
|
+
await this.addStepToArchive(step, archive);
|
|
32
|
+
}
|
|
33
|
+
const routerTemplate = this.createRouterTemplate(steps);
|
|
34
|
+
archive.append(routerTemplate, 'router.py');
|
|
35
|
+
(0, include_static_files_1.includeStaticFiles)(steps, this.builder, archive);
|
|
36
|
+
const size = await archive.finalize();
|
|
37
|
+
return { size, path: zipName };
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
throw new Error(`Failed to build Python API router: ${error}`);
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
if (fs_1.default.existsSync(tempSitePackages)) {
|
|
44
|
+
fs_1.default.rmSync(tempSitePackages, { recursive: true, force: true });
|
|
45
|
+
}
|
|
30
46
|
}
|
|
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
47
|
}
|
|
35
48
|
async build(step) {
|
|
36
49
|
const entrypointPath = step.filePath.replace(this.builder.projectDir, '');
|
|
37
50
|
const bundlePath = path_1.default.join('python', entrypointPath.replace(/(.*)\.py$/, '$1.zip'));
|
|
38
|
-
const normalizedEntrypointPath = entrypointPath.replace(/[.]step.py$/, '_step.py');
|
|
39
51
|
const outfile = path_1.default.join(constants_1.distDir, bundlePath);
|
|
52
|
+
this.builder.registerStep({ entrypointPath, bundlePath, step, type: 'python' });
|
|
53
|
+
this.listener.onBuildStart(step);
|
|
40
54
|
try {
|
|
41
|
-
// Create output directory
|
|
42
55
|
fs_1.default.mkdirSync(path_1.default.dirname(outfile), { recursive: true });
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
56
|
+
const archive = new archiver_1.Archiver(outfile);
|
|
57
|
+
const tempSitePackages = path_1.default.join(constants_1.distDir, `temp-python-packages-${Date.now()}`);
|
|
58
|
+
try {
|
|
59
|
+
await this.uvPackager.packageDependencies(tempSitePackages);
|
|
60
|
+
await this.waitForDirectoryReady(tempSitePackages);
|
|
61
|
+
await this.addStepToArchive(step, archive);
|
|
62
|
+
await this.addPackagesToArchive(archive, tempSitePackages);
|
|
63
|
+
(0, include_static_files_1.includeStaticFiles)([step], this.builder, archive);
|
|
64
|
+
const size = await archive.finalize();
|
|
65
|
+
this.listener.onBuildEnd(step, size);
|
|
51
66
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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`);
|
|
67
|
+
finally {
|
|
68
|
+
if (fs_1.default.existsSync(tempSitePackages)) {
|
|
69
|
+
fs_1.default.rmSync(tempSitePackages, { recursive: true, force: true });
|
|
70
|
+
}
|
|
60
71
|
}
|
|
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
72
|
}
|
|
66
73
|
catch (err) {
|
|
67
74
|
this.listener.onBuildError(step, err);
|
|
68
75
|
throw err;
|
|
69
76
|
}
|
|
70
77
|
}
|
|
71
|
-
async
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
.replace(
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
78
|
+
async addStepToArchive(step, archive) {
|
|
79
|
+
const normalizedPath = this.normalizeStepPath(step, false);
|
|
80
|
+
archive.append(fs_1.default.createReadStream(step.filePath), normalizedPath);
|
|
81
|
+
const internalFiles = await this.findInternalFiles(step.filePath);
|
|
82
|
+
for (const file of internalFiles) {
|
|
83
|
+
const fullPath = path_1.default.join(this.builder.projectDir, file);
|
|
84
|
+
if (fs_1.default.existsSync(fullPath) && fullPath !== step.filePath) {
|
|
85
|
+
const archivePath = file.replace(/\.step\.py$/, '_step.py');
|
|
86
|
+
archive.append(fs_1.default.createReadStream(fullPath), archivePath);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async addPackagesToArchive(archive, sitePackagesDir) {
|
|
91
|
+
if (!fs_1.default.existsSync(sitePackagesDir)) {
|
|
92
|
+
console.warn(`Warning: Site packages directory not found: ${sitePackagesDir}`);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
fs_1.default.accessSync(sitePackagesDir, fs_1.default.constants.R_OK);
|
|
87
97
|
}
|
|
88
|
-
|
|
98
|
+
catch (error) {
|
|
99
|
+
console.warn(`Warning: Cannot access site packages directory: ${sitePackagesDir}`);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const addDirectory = (dirPath, basePath = sitePackagesDir) => {
|
|
103
|
+
try {
|
|
104
|
+
const items = fs_1.default.readdirSync(dirPath);
|
|
105
|
+
for (const item of items) {
|
|
106
|
+
const fullPath = path_1.default.join(dirPath, item);
|
|
107
|
+
const relativePath = path_1.default.relative(basePath, fullPath);
|
|
108
|
+
if (this.shouldIgnoreFile(relativePath)) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
const stat = fs_1.default.statSync(fullPath);
|
|
113
|
+
if (stat.isDirectory()) {
|
|
114
|
+
addDirectory(fullPath, basePath);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
archive.append(fs_1.default.createReadStream(fullPath), relativePath);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
console.warn(`Warning: Could not process file ${fullPath}: ${error}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
console.warn(`Warning: Could not read directory ${dirPath}: ${error}`);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
addDirectory(sitePackagesDir);
|
|
130
|
+
}
|
|
131
|
+
shouldIgnoreFile(filePath) {
|
|
132
|
+
const ignorePatterns = [
|
|
133
|
+
/\.pyc$/,
|
|
134
|
+
/\.pyo$/,
|
|
135
|
+
/\.egg$/,
|
|
136
|
+
/\.egg-info$/,
|
|
137
|
+
/__pycache__/,
|
|
138
|
+
/\.dist-info$/,
|
|
139
|
+
/^tests?\//,
|
|
140
|
+
/^docs?\//,
|
|
141
|
+
/^examples?\//,
|
|
142
|
+
/\.pytest_cache/,
|
|
143
|
+
];
|
|
144
|
+
return ignorePatterns.some((pattern) => pattern.test(filePath));
|
|
145
|
+
}
|
|
146
|
+
normalizeStepPath(step, normalizePythonModulePath) {
|
|
147
|
+
let normalizedStepPath = step.filePath
|
|
148
|
+
.replace(/[.]step.py$/, '_step.py') // Replace .step.py with _step.py
|
|
149
|
+
.replace(`${this.builder.projectDir}/`, ''); // Remove the project directory from the path
|
|
150
|
+
const pathParts = normalizedStepPath.split(path_1.default.sep).map((part) => part
|
|
151
|
+
.replace(/^[0-9]+/g, '') // Remove numeric prefixes
|
|
152
|
+
.replace(/[^a-zA-Z0-9._]/g, '_') // Replace any non-alphanumeric characters (except dots) with underscores
|
|
153
|
+
.replace(/^_/, '')); // Remove leading underscore
|
|
154
|
+
normalizedStepPath = normalizePythonModulePath
|
|
155
|
+
? pathParts.join('.') // Convert path delimiter to dot (python module separator)
|
|
156
|
+
: '/' + pathParts.join(path_1.default.sep);
|
|
157
|
+
return normalizedStepPath;
|
|
158
|
+
}
|
|
159
|
+
createRouterTemplate(steps) {
|
|
160
|
+
const imports = steps
|
|
161
|
+
.map((step, index) => {
|
|
162
|
+
const moduleName = this.getModuleName(step);
|
|
163
|
+
return `from ${moduleName} import handler as route${index}_handler, config as route${index}_config`;
|
|
164
|
+
})
|
|
165
|
+
.join('\n');
|
|
166
|
+
const routerPaths = steps
|
|
167
|
+
.map((step, index) => {
|
|
168
|
+
const method = step.config.method.toUpperCase();
|
|
169
|
+
const path = step.config.path;
|
|
170
|
+
return ` '${method} ${path}': RouterPath('${step.config.name}', '${step.config.method.toLowerCase()}', route${index}_handler, route${index}_config)`;
|
|
171
|
+
})
|
|
172
|
+
.join(',\n');
|
|
173
|
+
return fs_1.default
|
|
89
174
|
.readFileSync(path_1.default.join(__dirname, 'router_template.py'), 'utf-8')
|
|
90
|
-
.replace('# {{imports}}',
|
|
91
|
-
.
|
|
92
|
-
.join('\n'))
|
|
93
|
-
.replace('# {{router paths}}', steps
|
|
94
|
-
.map((step, index) => `'${step.config.method} ${step.config.path}': RouterPath('${step.config.name}', '${step.config.method.toLowerCase()}', route${index}_handler, route${index}_config)`)
|
|
95
|
-
.join(',\n '));
|
|
96
|
-
archive.append(file, 'router.py');
|
|
97
|
-
(0, include_static_files_1.includeStaticFiles)(steps, this.builder, archive);
|
|
98
|
-
// Finalize the archive and wait for completion
|
|
99
|
-
const size = await archive.finalize();
|
|
100
|
-
return { size, path: zipName };
|
|
175
|
+
.replace('# {{imports}}', imports)
|
|
176
|
+
.replace('# {{router paths}}', routerPaths);
|
|
101
177
|
}
|
|
102
|
-
async
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
178
|
+
async findInternalFiles(entryFile) {
|
|
179
|
+
const files = [];
|
|
180
|
+
const visited = new Set();
|
|
181
|
+
const analyzeFile = (filePath) => {
|
|
182
|
+
if (visited.has(filePath) || !fs_1.default.existsSync(filePath)) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
visited.add(filePath);
|
|
186
|
+
files.push(path_1.default.relative(this.builder.projectDir, filePath));
|
|
187
|
+
try {
|
|
188
|
+
const content = fs_1.default.readFileSync(filePath, 'utf-8');
|
|
189
|
+
const importRegex = /^(?:from\s+([a-zA-Z_][a-zA-Z0-9_.]*)\s+import|import\s+([a-zA-Z_][a-zA-Z0-9_.]*))/gm;
|
|
190
|
+
let match;
|
|
191
|
+
while ((match = importRegex.exec(content)) !== null) {
|
|
192
|
+
const moduleName = match[1] || match[2]; // from X import Y ou import X
|
|
193
|
+
this.resolveModulePaths(moduleName, path_1.default.dirname(filePath)).forEach((possiblePath) => {
|
|
194
|
+
if (fs_1.default.existsSync(possiblePath)) {
|
|
195
|
+
analyzeFile(possiblePath);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
114
198
|
}
|
|
115
|
-
}
|
|
116
|
-
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
console.warn(`Could not analyze file: ${filePath}`);
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
analyzeFile(entryFile);
|
|
205
|
+
return files;
|
|
206
|
+
}
|
|
207
|
+
resolveModulePaths(moduleName, currentDir) {
|
|
208
|
+
const parts = moduleName.split('.');
|
|
209
|
+
const baseName = parts[0];
|
|
210
|
+
const subPath = parts.length > 1 ? path_1.default.join(...parts) : baseName;
|
|
211
|
+
return [
|
|
212
|
+
path_1.default.join(currentDir, `${baseName}.py`),
|
|
213
|
+
path_1.default.join(currentDir, baseName, '__init__.py'),
|
|
214
|
+
path_1.default.join(currentDir, `${subPath}.py`),
|
|
215
|
+
path_1.default.join(this.builder.projectDir, `${baseName}.py`),
|
|
216
|
+
path_1.default.join(this.builder.projectDir, baseName, '__init__.py'),
|
|
217
|
+
path_1.default.join(this.builder.projectDir, `${subPath}.py`),
|
|
218
|
+
path_1.default.join(this.builder.projectDir, subPath + '.py'),
|
|
219
|
+
path_1.default.join(this.builder.projectDir, subPath, '__init__.py'),
|
|
220
|
+
];
|
|
221
|
+
}
|
|
222
|
+
getModuleName(step) {
|
|
223
|
+
return this.normalizeStepPath(step, true).replace(/\.py$/, '').replace(/\//g, '.');
|
|
224
|
+
}
|
|
225
|
+
async waitForDirectoryReady(dirPath, maxRetries = 10, initialDelayMs = 10) {
|
|
226
|
+
let lastError = null;
|
|
227
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
228
|
+
try {
|
|
229
|
+
const exists = await fs_1.default.promises
|
|
230
|
+
.access(dirPath, fs_1.default.constants.F_OK)
|
|
231
|
+
.then(() => true)
|
|
232
|
+
.catch(() => false);
|
|
233
|
+
if (!exists) {
|
|
234
|
+
// Directory doesn't exist yet, wait
|
|
235
|
+
lastError = new Error(`Directory ${dirPath} does not exist yet`);
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
await fs_1.default.promises.access(dirPath, fs_1.default.constants.R_OK);
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
lastError = error;
|
|
244
|
+
}
|
|
245
|
+
if (i === maxRetries - 1) {
|
|
246
|
+
throw new Error(`Directory ${dirPath} is not accessible after ${maxRetries} attempts. ` +
|
|
247
|
+
`Last error: ${lastError?.message || 'Unknown error'}`);
|
|
248
|
+
}
|
|
249
|
+
// Exponential backoff: 10ms, 20ms, 40ms, 80ms, etc.
|
|
250
|
+
const delay = initialDelayMs * Math.pow(2, i);
|
|
251
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(delay, 1000))); // Cap at 1 second
|
|
252
|
+
}
|
|
117
253
|
}
|
|
118
254
|
}
|
|
119
255
|
exports.PythonBuilder = PythonBuilder;
|
|
@@ -0,0 +1,13 @@
|
|
|
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
|
+
private runCommand;
|
|
12
|
+
packageDependencies(targetDir: string): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
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: process.env.MOTIA_PYTHON_VERSION || '3.13',
|
|
12
|
+
platform: process.env.MOTIA_PLATFORM || 'x86_64-manylinux2014',
|
|
13
|
+
onlyBinary: process.env.MOTIA_ONLY_BINARY !== 'false',
|
|
14
|
+
};
|
|
15
|
+
class UvPackager {
|
|
16
|
+
constructor(projectDir, config = exports.defaultUvConfig) {
|
|
17
|
+
this.projectDir = projectDir;
|
|
18
|
+
this.config = config;
|
|
19
|
+
}
|
|
20
|
+
async runCommand(command, args, options) {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
const child = (0, child_process_1.spawn)(command, args, {
|
|
23
|
+
cwd: options?.cwd,
|
|
24
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
25
|
+
});
|
|
26
|
+
let stdout = '';
|
|
27
|
+
let stderr = '';
|
|
28
|
+
child.stdout?.on('data', (data) => {
|
|
29
|
+
stdout += data.toString();
|
|
30
|
+
if (options?.showOutput) {
|
|
31
|
+
process.stdout.write(data);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
child.stderr?.on('data', (data) => {
|
|
35
|
+
stderr += data.toString();
|
|
36
|
+
});
|
|
37
|
+
child.on('close', (code) => {
|
|
38
|
+
if (code === 0) {
|
|
39
|
+
resolve(stdout);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
const errorPrefix = `Command '${command}'`;
|
|
43
|
+
reject(new Error(`${errorPrefix} failed: ${stderr || stdout}`));
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
child.on('error', (error) => {
|
|
47
|
+
reject(new Error(`Failed to spawn ${command}: ${error.message}`));
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
async packageDependencies(targetDir) {
|
|
52
|
+
if (!fs_1.default.existsSync(targetDir)) {
|
|
53
|
+
fs_1.default.mkdirSync(targetDir, { recursive: true });
|
|
54
|
+
}
|
|
55
|
+
const requirementsFile = path_1.default.join(this.projectDir, 'requirements.txt');
|
|
56
|
+
if (!fs_1.default.existsSync(requirementsFile)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const args = [
|
|
60
|
+
'pip',
|
|
61
|
+
'install',
|
|
62
|
+
'--target',
|
|
63
|
+
targetDir,
|
|
64
|
+
'--requirement',
|
|
65
|
+
requirementsFile,
|
|
66
|
+
'--python-version',
|
|
67
|
+
this.config.pythonVersion || '3.13',
|
|
68
|
+
'--python-platform',
|
|
69
|
+
this.config.platform || 'x86_64-manylinux2014',
|
|
70
|
+
];
|
|
71
|
+
if (this.config.onlyBinary) {
|
|
72
|
+
args.push('--only-binary=:all:');
|
|
73
|
+
}
|
|
74
|
+
await this.runCommand('uv', args, { cwd: this.projectDir });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
exports.UvPackager = UvPackager;
|
|
@@ -13,11 +13,13 @@ cli_1.cloudCli
|
|
|
13
13
|
.command('deploy')
|
|
14
14
|
.description('Deploy a new version to Motia Cloud')
|
|
15
15
|
.requiredOption('-k, --api-key <key>', 'The API key for authentication', process.env.MOTIA_API_KEY)
|
|
16
|
-
.requiredOption('-v, --version-name <version>', 'The version to deploy')
|
|
17
16
|
.option('-p, --project-id <id>', 'Project ID (Deprecated)')
|
|
17
|
+
.option('-n, --project-name <name>', 'Project name (used when creating a new project)')
|
|
18
18
|
.option('-s, --environment-id <id>', 'Environment ID', process.env.MOTIA_ENVIRONMENT_ID)
|
|
19
|
+
.option('--environment-name <name>', 'Environment name')
|
|
19
20
|
.option('-e, --env-file <path>', 'Path to environment file')
|
|
20
|
-
.
|
|
21
|
+
.requiredOption('-v, --version-name <version>', 'The version to deploy')
|
|
22
|
+
.option('-d, --version-description <description>', 'The description of the version')
|
|
21
23
|
.action((0, config_utils_1.handler)(async (arg, context) => {
|
|
22
24
|
const listener = new cli_listener_1.CliListener(context);
|
|
23
25
|
const builder = await (0, build_1.build)(listener);
|
|
@@ -30,9 +32,11 @@ cli_1.cloudCli
|
|
|
30
32
|
const deployment = await cloud_api_1.cloudApi
|
|
31
33
|
.createDeployment({
|
|
32
34
|
apiKey: arg.apiKey,
|
|
33
|
-
versionName: arg.versionName,
|
|
34
|
-
environmentId: arg.environmentId,
|
|
35
35
|
projectName: arg.projectName,
|
|
36
|
+
environmentId: arg.environmentId,
|
|
37
|
+
environmentName: arg.environmentName,
|
|
38
|
+
versionName: arg.versionName,
|
|
39
|
+
versionDescription: arg.versionDescription,
|
|
36
40
|
})
|
|
37
41
|
.catch((error) => {
|
|
38
42
|
context.log('creating-deployment', (message) => message.tag('failed').append('Failed to create deployment'));
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
type CreateDeploymentRequest = {
|
|
2
2
|
apiKey: string;
|
|
3
|
+
projectName?: string;
|
|
3
4
|
environmentId?: string;
|
|
5
|
+
environmentName?: string;
|
|
4
6
|
versionName: string;
|
|
5
|
-
|
|
7
|
+
versionDescription?: string;
|
|
6
8
|
};
|
|
7
9
|
type CreateDeploymentResult = {
|
|
8
10
|
deploymentId: string;
|
|
@@ -11,9 +11,11 @@ export declare const cloudApi: {
|
|
|
11
11
|
}>;
|
|
12
12
|
createDeployment: (request: {
|
|
13
13
|
apiKey: string;
|
|
14
|
+
projectName?: string;
|
|
14
15
|
environmentId?: string;
|
|
16
|
+
environmentName?: string;
|
|
15
17
|
versionName: string;
|
|
16
|
-
|
|
18
|
+
versionDescription?: string;
|
|
17
19
|
}) => Promise<{
|
|
18
20
|
deploymentId: string;
|
|
19
21
|
deploymentToken: string;
|
|
@@ -2,7 +2,7 @@ from datetime import datetime, timezone
|
|
|
2
2
|
|
|
3
3
|
config = {
|
|
4
4
|
"type": "cron",
|
|
5
|
-
"cron": "
|
|
5
|
+
"cron": "0 0 * * 1", # run once every Monday at midnight
|
|
6
6
|
"name": "StateAuditJob",
|
|
7
7
|
"description": "Checks the state for orders that are not complete and have a ship date in the past",
|
|
8
8
|
"emits": ["notification"],
|
|
@@ -2,7 +2,7 @@ import { CronConfig, Handlers } from 'motia'
|
|
|
2
2
|
|
|
3
3
|
export const config: CronConfig = {
|
|
4
4
|
type: 'cron',
|
|
5
|
-
cron: '
|
|
5
|
+
cron: '0 0 * * 1', // run once every Monday at midnight
|
|
6
6
|
name: 'StateAuditJob',
|
|
7
7
|
description: 'Checks the state for orders that are not complete and have a ship date in the past',
|
|
8
8
|
emits: ['notification'],
|
|
@@ -9,7 +9,9 @@ const path_1 = __importDefault(require("path"));
|
|
|
9
9
|
const colors_1 = __importDefault(require("colors"));
|
|
10
10
|
const AI_GUIDES_VERSION = '1.0.0';
|
|
11
11
|
const CURSOR_RULES_SOURCE = path_1.default.join(__dirname, '../../dot-files/.cursor/rules');
|
|
12
|
+
const CLAUDE_SOURCE = path_1.default.join(__dirname, '../../dot-files/.claude');
|
|
12
13
|
const TARGET_DIR = '.cursor/rules';
|
|
14
|
+
const CLAUDE_TARGET_DIR = '.claude';
|
|
13
15
|
async function handleAIGuides(options) {
|
|
14
16
|
if (options.version) {
|
|
15
17
|
console.log(colors_1.default.blue(`Motia AI Development Guides v${AI_GUIDES_VERSION}`));
|
|
@@ -50,11 +52,11 @@ async function pullAIGuides(force) {
|
|
|
50
52
|
}
|
|
51
53
|
// First, copy the essential AI development guides
|
|
52
54
|
console.log(colors_1.default.blue('🤖 Installing AI Development Guides...'));
|
|
53
|
-
let claudePath = path_1.default.join(__dirname, '../../dot-files/CLAUDE.md');
|
|
55
|
+
let claudePath = path_1.default.join(__dirname, '../../dot-files/.claude/CLAUDE.md');
|
|
54
56
|
let agentsPath = path_1.default.join(__dirname, '../../dot-files/AGENTS.md');
|
|
55
57
|
// Try node_modules paths if local paths don't exist
|
|
56
58
|
if (!fs_1.default.existsSync(claudePath)) {
|
|
57
|
-
claudePath = path_1.default.join('node_modules', '@motiadev/snap', 'dist', 'dot-files', 'CLAUDE.md');
|
|
59
|
+
claudePath = path_1.default.join('node_modules', '@motiadev/snap', 'dist', 'dot-files', '.claude', 'CLAUDE.md');
|
|
58
60
|
}
|
|
59
61
|
if (!fs_1.default.existsSync(agentsPath)) {
|
|
60
62
|
agentsPath = path_1.default.join('node_modules', '@motiadev/snap', 'dist', 'dot-files', 'AGENTS.md');
|
|
@@ -89,18 +91,38 @@ async function pullAIGuides(force) {
|
|
|
89
91
|
}
|
|
90
92
|
}
|
|
91
93
|
await copyDirectory(sourcePath, TARGET_DIR);
|
|
94
|
+
// Copy Claude IDE configurations
|
|
92
95
|
console.log('');
|
|
93
|
-
console.log(colors_1.default.
|
|
96
|
+
console.log(colors_1.default.blue('🧠 Installing Claude IDE Configurations...'));
|
|
97
|
+
if (!fs_1.default.existsSync(CLAUDE_TARGET_DIR) || force) {
|
|
98
|
+
if (fs_1.default.existsSync(CLAUDE_TARGET_DIR) && force) {
|
|
99
|
+
fs_1.default.rmSync(CLAUDE_TARGET_DIR, { recursive: true, force: true });
|
|
100
|
+
}
|
|
101
|
+
if (fs_1.default.existsSync(CLAUDE_SOURCE)) {
|
|
102
|
+
await copyDirectory(CLAUDE_SOURCE, CLAUDE_TARGET_DIR);
|
|
103
|
+
console.log(colors_1.default.green('✅ Claude IDE configurations installed'));
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
console.log(colors_1.default.yellow('⚠️ Claude configurations not found - this is optional'));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
console.log(colors_1.default.yellow('⚠️ Claude configurations already exist. Use --force to overwrite.'));
|
|
111
|
+
}
|
|
112
|
+
console.log('');
|
|
113
|
+
console.log(colors_1.default.green('🎉 AI Development Guides, Cursor Rules & Claude Configs installed successfully!'));
|
|
94
114
|
console.log('');
|
|
95
115
|
console.log(colors_1.default.blue('📋 What you got:'));
|
|
96
116
|
console.log(colors_1.default.gray(' • AGENTS.md - Universal guide for ANY AI tool (project root)'));
|
|
97
117
|
console.log(colors_1.default.gray(' • CLAUDE.md - Specific guide for Claude AI assistant (project root)'));
|
|
98
118
|
console.log(colors_1.default.gray(` • Cursor Rules - IDE-specific patterns (${TARGET_DIR})`));
|
|
119
|
+
console.log(colors_1.default.gray(` • Claude Configs - Claude IDE configurations (${CLAUDE_TARGET_DIR})`));
|
|
99
120
|
console.log('');
|
|
100
121
|
console.log(colors_1.default.blue('📁 File locations:'));
|
|
101
122
|
console.log(colors_1.default.gray(` ./AGENTS.md`));
|
|
102
123
|
console.log(colors_1.default.gray(` ./CLAUDE.md`));
|
|
103
124
|
console.log(colors_1.default.gray(` ${path_1.default.resolve(TARGET_DIR)}/`));
|
|
125
|
+
console.log(colors_1.default.gray(` ${path_1.default.resolve(CLAUDE_TARGET_DIR)}/`));
|
|
104
126
|
console.log('');
|
|
105
127
|
console.log(colors_1.default.blue('🚀 Next steps:'));
|
|
106
128
|
console.log(colors_1.default.gray(' 1. Open AGENTS.md to understand Motia development patterns'));
|
|
@@ -214,6 +236,13 @@ async function removeAIGuides() {
|
|
|
214
236
|
console.log(colors_1.default.yellow('🗑️ Removed .cursor/rules/'));
|
|
215
237
|
removed = true;
|
|
216
238
|
}
|
|
239
|
+
// Remove Claude configurations directory
|
|
240
|
+
if (fs_1.default.existsSync(CLAUDE_TARGET_DIR)) {
|
|
241
|
+
console.log(colors_1.default.yellow('🗑️ Removing Claude configurations...'));
|
|
242
|
+
fs_1.default.rmSync(CLAUDE_TARGET_DIR, { recursive: true, force: true });
|
|
243
|
+
console.log(colors_1.default.yellow('🗑️ Removed .claude/'));
|
|
244
|
+
removed = true;
|
|
245
|
+
}
|
|
217
246
|
if (removed) {
|
|
218
247
|
console.log(colors_1.default.green('✅ AI development guides and cursor rules removed successfully!'));
|
|
219
248
|
}
|
package/dist/cjs/install.js
CHANGED
|
@@ -11,6 +11,7 @@ const activate_python_env_1 = require("./utils/activate-python-env");
|
|
|
11
11
|
const install_lambda_python_packages_1 = require("./utils/install-lambda-python-packages");
|
|
12
12
|
const generate_locked_data_1 = require("./generate-locked-data");
|
|
13
13
|
const python_version_utils_1 = require("./utils/python-version-utils");
|
|
14
|
+
const ensure_uv_1 = require("./utils/ensure-uv");
|
|
14
15
|
const pythonInstall = async ({ baseDir, isVerbose = false, pythonVersion = '3.13', }) => {
|
|
15
16
|
const venvPath = path_1.default.join(baseDir, 'python_modules');
|
|
16
17
|
console.log('📦 Installing Python dependencies...', venvPath);
|
|
@@ -30,6 +31,10 @@ const pythonInstall = async ({ baseDir, isVerbose = false, pythonVersion = '3.13
|
|
|
30
31
|
await (0, execute_command_1.executeCommand)(`${pythonCmd} -m venv python_modules`, baseDir);
|
|
31
32
|
}
|
|
32
33
|
(0, activate_python_env_1.activatePythonVenv)({ baseDir, isVerbose, pythonVersion });
|
|
34
|
+
// Ensure UV is installed
|
|
35
|
+
console.log('🔧 Checking UV installation...');
|
|
36
|
+
await (0, ensure_uv_1.ensureUvInstalled)();
|
|
37
|
+
console.log('✅ UV is available');
|
|
33
38
|
(0, install_lambda_python_packages_1.installLambdaPythonPackages)({ isVerbose, requirementsList });
|
|
34
39
|
// Install requirements
|
|
35
40
|
console.log('📥 Installing Python dependencies...');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const ensureUvInstalled: () => Promise<void>;
|