motia 0.15.5-beta.174-720958 → 0.15.5-beta.174-096320

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 (33) hide show
  1. package/dist/cli.mjs +9 -1
  2. package/dist/cli.mjs.map +1 -1
  3. package/dist/cloud/config-utils.mjs +1 -1
  4. package/dist/cloud/config-utils.mjs.map +1 -1
  5. package/dist/cloud/new-deployment/build.mjs +11 -1
  6. package/dist/cloud/new-deployment/build.mjs.map +1 -1
  7. package/dist/create/index.mjs +28 -12
  8. package/dist/create/index.mjs.map +1 -1
  9. package/dist/cursor-rules/dot-files/.claude/agents/motia-developer.md +20 -11
  10. package/dist/cursor-rules/dot-files/.cursor/architecture/architecture.mdc +124 -28
  11. package/dist/cursor-rules/dot-files/.cursor/rules/motia/motia-config.mdc +66 -0
  12. package/dist/cursor-rules/dot-files/AGENTS.md +31 -16
  13. package/dist/cursor-rules/dot-files/CLAUDE.md +7 -2
  14. package/dist/cursor-rules/dot-files/opencode.json +1 -0
  15. package/dist/dev.mjs +8 -2
  16. package/dist/dev.mjs.map +1 -1
  17. package/dist/generate-locked-data.d.mts.map +1 -1
  18. package/dist/generate-locked-data.mjs +0 -2
  19. package/dist/generate-locked-data.mjs.map +1 -1
  20. package/dist/install.mjs +1 -1
  21. package/dist/plugins/install-plugin-dependencies.mjs +4 -3
  22. package/dist/plugins/install-plugin-dependencies.mjs.map +1 -1
  23. package/dist/start.mjs +8 -2
  24. package/dist/start.mjs.map +1 -1
  25. package/dist/utils/activate-python-env.mjs +9 -15
  26. package/dist/utils/activate-python-env.mjs.map +1 -1
  27. package/dist/utils/get-package-manager.mjs +42 -5
  28. package/dist/utils/get-package-manager.mjs.map +1 -1
  29. package/dist/utils/validate-python-environment.mjs +81 -0
  30. package/dist/utils/validate-python-environment.mjs.map +1 -0
  31. package/dist/watcher.mjs +1 -3
  32. package/dist/watcher.mjs.map +1 -1
  33. package/package.json +8 -8
@@ -7,6 +7,43 @@ alwaysApply: false
7
7
 
8
8
  The `motia.config.ts` file is the central configuration file for your Motia application. It allows you to customize plugins, adapters, stream authentication, and Express app settings.
9
9
 
10
+ ## Critical Requirement: package.json Must Use ES Modules
11
+
12
+ **All Motia projects MUST have `"type": "module"` in their `package.json`.**
13
+
14
+ Motia uses ES modules internally and requires this setting to function correctly. Without it, you may encounter import/export errors during runtime.
15
+
16
+ ### Correct package.json Setup
17
+
18
+ ```json
19
+ {
20
+ "name": "my-motia-project",
21
+ "description": "My Motia application",
22
+ "type": "module",
23
+ "scripts": {
24
+ "postinstall": "motia install",
25
+ "dev": "motia dev",
26
+ "start": "motia start",
27
+ "build": "motia build",
28
+ "generate-types": "motia generate-types"
29
+ }
30
+ }
31
+ ```
32
+
33
+ ### Migration from Existing Projects
34
+
35
+ If you have an existing Motia project, ensure you add `"type": "module"` to your `package.json`:
36
+
37
+ ```json
38
+ {
39
+ "name": "my-project",
40
+ "type": "module", // ← Add this line
41
+ "scripts": {
42
+ "dev": "motia dev"
43
+ }
44
+ }
45
+ ```
46
+
10
47
  ## Creating the Configuration File
11
48
 
12
49
  Create a `motia.config.ts` file in the root of your project:
@@ -157,6 +194,21 @@ export default config({
157
194
 
158
195
  ### Creating a Local Plugin
159
196
 
197
+ **Project structure:**
198
+ ```
199
+ project/
200
+ ├── src/ # Steps can be in /src or /steps
201
+ │ └── api/
202
+ │ └── example.step.ts
203
+ ├── plugins/
204
+ │ └── my-plugin/
205
+ │ ├── components/
206
+ │ │ └── my-plugin-panel.tsx
207
+ │ └── index.ts
208
+ └── motia.config.ts
209
+ ```
210
+
211
+ **Plugin implementation (`plugins/my-plugin/index.ts`):**
160
212
  ```typescript
161
213
  import path from 'node:path'
162
214
  import { config, type MotiaPlugin, type MotiaPluginContext } from 'motia'
@@ -195,6 +247,20 @@ export default config({
195
247
  })
196
248
  ```
197
249
 
250
+ ### Common Plugin Errors
251
+
252
+ **Error: Component not found**
253
+ - **Cause**: `packageName` doesn't match the actual folder structure
254
+ - **Solution**: Ensure `packageName: '~/plugins/my-plugin'` matches `plugins/my-plugin/` folder
255
+
256
+ **Error: Plugin not loading in workbench**
257
+ - **Cause**: Plugin function not exported correctly
258
+ - **Solution**: Use `export default function` in plugin's `index.ts`
259
+
260
+ **Error: Module resolution failed**
261
+ - **Cause**: Using incorrect casing in folder/file names
262
+ - **Solution**: Use `kebab-case` for folders/files, `PascalCase` for React components
263
+
198
264
  ## Adapters
199
265
 
200
266
  Adapters allow you to customize the underlying infrastructure for state management, event handling, cron jobs, and streams. This is useful for horizontal scaling or using custom storage backends.
@@ -40,6 +40,7 @@ These guides are written in markdown and can be read by any AI coding tool. The
40
40
 
41
41
  Read these files in `.cursor/rules/motia/` for detailed patterns:
42
42
 
43
+ - **`motia-config.mdc`** - Essential project setup, package.json requirements, plugin naming
43
44
  - **`api-steps.mdc`** - Creating HTTP endpoints with schemas, validation, and middleware
44
45
  - **`event-steps.mdc`** - Background task processing and event-driven workflows
45
46
  - **`cron-steps.mdc`** - Scheduled tasks with cron expressions
@@ -62,31 +63,44 @@ Architecture guides in `.cursor/architecture/`:
62
63
 
63
64
  ### Project Structure
64
65
 
66
+ Motia discovers steps from both `/src` and `/steps` folders. Modern projects typically use `/src`:
67
+
68
+ **Recommended Structure (using `/src`):**
65
69
  ```
66
70
  project/
67
71
  ├── .cursor/rules/ # DETAILED GUIDES - Read these first!
68
- ├── steps/ # All step definitions
69
- │ ├── api/ # API endpoints
70
- │ │ └── api.step.ts
71
- │ │ └── api.step.js
72
- │ │ └── api_step.py
73
- │ ├── events/ # Events
74
- │ │ └── events.step.ts
75
- │ │ └── events.step.js
76
- │ │ └── events_step.py
77
- │ └── cron/ # Scheduled tasks
78
- │ │ └── cron.step.ts
79
- │ │ └── cron.step.js
80
- │ │ └── cron_step.py
81
- ├── middlewares/ # Reusable middleware
82
- │ └── middleware.middleware.ts
83
72
  ├── src/
73
+ │ ├── api/ # API endpoints
74
+ │ │ ├── users.step.ts
75
+ │ │ ├── orders.step.js
76
+ │ │ └── products_step.py
77
+ │ ├── events/ # Event handlers
78
+ │ │ ├── order-processing.step.ts
79
+ │ │ └── notifications_step.py
80
+ │ ├── cron/ # Scheduled tasks
81
+ │ │ └── cleanup.step.ts
84
82
  │ ├── services/ # Business logic
83
+ │ ├── repositories/ # Data access
85
84
  │ └── utils/ # Utilities
86
- ├── config.yml # Motia configuration
85
+ ├── middlewares/ # Reusable middleware
86
+ │ └── auth.middleware.ts
87
+ ├── motia.config.ts # Motia configuration
87
88
  └── types.d.ts # Auto-generated types
88
89
  ```
89
90
 
91
+ **Alternative Structure (using `/steps`):**
92
+ ```
93
+ project/
94
+ ├── steps/ # Step definitions
95
+ │ ├── api/
96
+ │ ├── events/
97
+ │ └── cron/
98
+ ├── src/
99
+ │ ├── services/
100
+ │ └── utils/
101
+ └── motia.config.ts
102
+ ```
103
+
90
104
  ### Step Naming Conventions
91
105
 
92
106
  **TypeScript/JavaScript:** `my-step.step.ts` (kebab-case)
@@ -194,6 +208,7 @@ When working on Motia projects, follow this pattern:
194
208
 
195
209
  ## Critical Rules
196
210
 
211
+ - **ALWAYS** ensure `package.json` has `"type": "module"` (read `motia-config.mdc` for details)
197
212
  - **ALWAYS** read `.cursor/rules/` guides before writing step code
198
213
  - **ALWAYS** run `npx motia generate-types` after modifying configs
199
214
  - **ALWAYS** list emits in config before using them in handlers
@@ -12,7 +12,7 @@ This project has detailed development guides in **`.cursor/rules/`** directory.
12
12
 
13
13
  **A pre-configured subagent is ready!**
14
14
 
15
- The `motia-developer` subagent in `.claude/agents/` automatically references all 10 cursor rules when coding.
15
+ The `motia-developer` subagent in `.claude/agents/` automatically references all 11 cursor rules when coding.
16
16
 
17
17
  Use it: `/agents` → select `motia-developer`
18
18
 
@@ -27,10 +27,13 @@ Read .cursor/rules/motia/api-steps.mdc and create an API endpoint
27
27
  for user registration following the patterns shown.
28
28
  ```
29
29
 
30
- ## Available Guides (10 Comprehensive Files)
30
+ ## Available Guides (11 Comprehensive Files)
31
31
 
32
32
  All guides in `.cursor/rules/` with **TypeScript, JavaScript, and Python** examples:
33
33
 
34
+ **Configuration** (`.cursor/rules/motia/`):
35
+ - `motia-config.mdc` - Essential project setup, package.json requirements, plugin naming
36
+
34
37
  **Step Types** (`.cursor/rules/motia/`):
35
38
  - `api-steps.mdc`, `event-steps.mdc`, `cron-steps.mdc`
36
39
 
@@ -45,6 +48,8 @@ All guides in `.cursor/rules/` with **TypeScript, JavaScript, and Python** examp
45
48
 
46
49
  See `AGENTS.md` in this directory for a quick overview and links to specific guides.
47
50
 
51
+ **Important**: Motia discovers steps from both `/src` and `/steps` folders. Modern projects use `/src` for a familiar structure.
52
+
48
53
  ## Key Commands
49
54
 
50
55
  ```bash
@@ -3,6 +3,7 @@
3
3
  "description": "Motia event-driven backend framework project",
4
4
  "rules": ["AGENTS.md"],
5
5
  "context": [
6
+ ".cursor/rules/motia/motia-config.mdc",
6
7
  ".cursor/rules/motia/api-steps.mdc",
7
8
  ".cursor/rules/motia/event-steps.mdc",
8
9
  ".cursor/rules/motia/cron-steps.mdc",
package/dist/dev.mjs CHANGED
@@ -1,7 +1,8 @@
1
- import { activatePythonVenv } from "./utils/activate-python-env.mjs";
2
1
  import { generateLockedData, getStepFiles, getStreamFiles } from "./generate-locked-data.mjs";
3
2
  import { version } from "./version.mjs";
4
3
  import { identifyUser } from "./utils/analytics.mjs";
4
+ import { activatePythonVenv } from "./utils/activate-python-env.mjs";
5
+ import { validatePythonEnvironment } from "./utils/validate-python-environment.mjs";
5
6
  import { loadMotiaConfig } from "./load-motia-config.mjs";
6
7
  import { deployEndpoints } from "./cloud/endpoints.mjs";
7
8
  import { isTutorialDisabled, workbenchBase } from "./constants.mjs";
@@ -31,7 +32,12 @@ const dev = async (port, hostname, disableVerbose, enableMermaid, motiaFileStora
31
32
  total_step_files: stepFiles.length,
32
33
  project_name: getProjectIdentifier(baseDir)
33
34
  });
34
- if (hasPythonFiles) {
35
+ const pythonValidation = await validatePythonEnvironment({
36
+ baseDir,
37
+ hasPythonFiles
38
+ });
39
+ if (!pythonValidation.success) process.exit(1);
40
+ if (pythonValidation.hasPythonFiles) {
35
41
  activatePythonVenv({
36
42
  baseDir,
37
43
  isVerbose
package/dist/dev.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"dev.mjs","names":["redisClient: RedisClientType","plugins: MotiaPlugin[]"],"sources":["../src/dev.ts"],"sourcesContent":["import { flush } from '@amplitude/analytics-node'\nimport { BullMQEventAdapter } from '@motiadev/adapter-bullmq-events'\nimport { RedisCronAdapter } from '@motiadev/adapter-redis-cron'\nimport { RedisStateAdapter } from '@motiadev/adapter-redis-state'\nimport { RedisStreamAdapterManager } from '@motiadev/adapter-redis-streams'\nimport {\n createMermaidGenerator,\n createServer,\n getProjectIdentifier,\n type MotiaPlugin,\n trackEvent,\n} from '@motiadev/core'\nimport type { RedisClientType } from 'redis'\nimport { deployEndpoints } from './cloud/endpoints'\nimport { isTutorialDisabled, workbenchBase } from './constants'\nimport { createDevWatchers } from './dev-watchers'\nimport { generateLockedData, getStepFiles, getStreamFiles } from './generate-locked-data'\nimport { loadMotiaConfig } from './load-motia-config'\nimport { processPlugins } from './plugins'\nimport { getRedisClient, getRedisConnectionInfo, stopRedisConnection } from './redis/connection'\nimport { activatePythonVenv } from './utils/activate-python-env'\nimport { identifyUser } from './utils/analytics'\nimport { version } from './version'\n\nexport const dev = async (\n port: number,\n hostname: string,\n disableVerbose: boolean,\n enableMermaid: boolean,\n motiaFileStorageDir?: string,\n): Promise<void> => {\n const baseDir = process.cwd()\n const isVerbose = !disableVerbose\n\n identifyUser()\n\n const stepFiles = [...getStepFiles(baseDir), ...getStreamFiles(baseDir)]\n const hasPythonFiles = stepFiles.some((file) => file.endsWith('.py'))\n\n trackEvent('dev_server_started', {\n port,\n verbose_mode: isVerbose,\n mermaid_enabled: enableMermaid,\n has_python_files: hasPythonFiles,\n total_step_files: stepFiles.length,\n project_name: getProjectIdentifier(baseDir),\n })\n\n if (hasPythonFiles) {\n activatePythonVenv({ baseDir, isVerbose })\n trackEvent('python_environment_activated')\n }\n\n const motiaFileStoragePath = motiaFileStorageDir || '.motia'\n\n const appConfig = await loadMotiaConfig(baseDir)\n\n const redisClient: RedisClientType = await getRedisClient(motiaFileStoragePath, appConfig)\n\n const adapters = {\n eventAdapter:\n appConfig.adapters?.events ||\n new BullMQEventAdapter({\n connection: getRedisConnectionInfo(),\n prefix: 'motia:events',\n }),\n cronAdapter: appConfig.adapters?.cron || new RedisCronAdapter(redisClient),\n streamAdapter: appConfig.adapters?.streams || new RedisStreamAdapterManager(redisClient),\n }\n\n const lockedData = await generateLockedData({\n projectDir: baseDir,\n streamAdapter: adapters.streamAdapter,\n redisClient,\n streamAuth: appConfig.streamAuth,\n })\n\n const state = appConfig.adapters?.state || new RedisStateAdapter(redisClient)\n\n const config = { isVerbose }\n\n const motiaServer = createServer(lockedData, state, config, adapters, appConfig.app)\n const watcher = createDevWatchers(lockedData, motiaServer, motiaServer.motiaEventManager, motiaServer.cronManager)\n const plugins: MotiaPlugin[] = await processPlugins(motiaServer)\n\n // Initialize mermaid generator\n if (enableMermaid) {\n const mermaidGenerator = createMermaidGenerator(baseDir)\n mermaidGenerator.initialize(lockedData)\n trackEvent('mermaid_generator_initialized')\n }\n\n deployEndpoints(motiaServer, lockedData)\n\n motiaServer.app.get('/__motia', (_, res) => {\n const meta = {\n version,\n isDev: true,\n isTutorialDisabled,\n workbenchBase,\n }\n\n res //\n .header('Access-Control-Allow-Origin', '*')\n .header('Access-Control-Allow-Private-Network', 'true')\n .status(200)\n .json(meta)\n })\n\n trackEvent('dev_server_ready', {\n port,\n flows_count: lockedData.flows?.length || 0,\n steps_count: lockedData.activeSteps?.length || 0,\n flows: Object.keys(lockedData.flows || {}),\n steps: lockedData.activeSteps.map((step) => step.config.name),\n streams: Object.keys(lockedData.getStreams() || {}),\n runtime_version: version,\n environment: process.env.NODE_ENV || 'development',\n })\n\n const { applyMiddleware } = await import('@motiadev/workbench/middleware')\n\n await applyMiddleware({\n app: motiaServer.app,\n port,\n workbenchBase,\n plugins: plugins.flatMap((item) => item.workbench),\n })\n\n motiaServer.server.listen(port, hostname)\n console.log('🚀 Server ready and listening on port', port)\n console.log(`🔗 Open http://localhost:${port}${workbenchBase} to open workbench 🛠️`)\n\n process.on('SIGTERM', async () => {\n trackEvent('dev_server_shutdown', { reason: 'SIGTERM' })\n motiaServer.server.close()\n await watcher.stop()\n await stopRedisConnection()\n await flush().promise\n process.exit(0)\n })\n\n process.on('SIGINT', async () => {\n trackEvent('dev_server_shutdown', { reason: 'SIGINT' })\n motiaServer.server.close()\n await watcher.stop()\n await stopRedisConnection()\n await flush().promise\n process.exit(0)\n })\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"dev.mjs","names":["redisClient: RedisClientType","plugins: MotiaPlugin[]"],"sources":["../src/dev.ts"],"sourcesContent":["import { flush } from '@amplitude/analytics-node'\nimport { BullMQEventAdapter } from '@motiadev/adapter-bullmq-events'\nimport { RedisCronAdapter } from '@motiadev/adapter-redis-cron'\nimport { RedisStateAdapter } from '@motiadev/adapter-redis-state'\nimport { RedisStreamAdapterManager } from '@motiadev/adapter-redis-streams'\nimport {\n createMermaidGenerator,\n createServer,\n getProjectIdentifier,\n type MotiaPlugin,\n trackEvent,\n} from '@motiadev/core'\nimport type { RedisClientType } from 'redis'\nimport { deployEndpoints } from './cloud/endpoints'\nimport { isTutorialDisabled, workbenchBase } from './constants'\nimport { createDevWatchers } from './dev-watchers'\nimport { generateLockedData, getStepFiles, getStreamFiles } from './generate-locked-data'\nimport { loadMotiaConfig } from './load-motia-config'\nimport { processPlugins } from './plugins'\nimport { getRedisClient, getRedisConnectionInfo, stopRedisConnection } from './redis/connection'\nimport { activatePythonVenv } from './utils/activate-python-env'\nimport { identifyUser } from './utils/analytics'\nimport { validatePythonEnvironment } from './utils/validate-python-environment'\nimport { version } from './version'\n\nexport const dev = async (\n port: number,\n hostname: string,\n disableVerbose: boolean,\n enableMermaid: boolean,\n motiaFileStorageDir?: string,\n): Promise<void> => {\n const baseDir = process.cwd()\n const isVerbose = !disableVerbose\n\n identifyUser()\n\n const stepFiles = [...getStepFiles(baseDir), ...getStreamFiles(baseDir)]\n const hasPythonFiles = stepFiles.some((file) => file.endsWith('.py'))\n\n trackEvent('dev_server_started', {\n port,\n verbose_mode: isVerbose,\n mermaid_enabled: enableMermaid,\n has_python_files: hasPythonFiles,\n total_step_files: stepFiles.length,\n project_name: getProjectIdentifier(baseDir),\n })\n\n const pythonValidation = await validatePythonEnvironment({ baseDir, hasPythonFiles })\n if (!pythonValidation.success) {\n process.exit(1)\n }\n\n if (pythonValidation.hasPythonFiles) {\n activatePythonVenv({ baseDir, isVerbose })\n trackEvent('python_environment_activated')\n }\n\n const motiaFileStoragePath = motiaFileStorageDir || '.motia'\n\n const appConfig = await loadMotiaConfig(baseDir)\n\n const redisClient: RedisClientType = await getRedisClient(motiaFileStoragePath, appConfig)\n\n const adapters = {\n eventAdapter:\n appConfig.adapters?.events ||\n new BullMQEventAdapter({\n connection: getRedisConnectionInfo(),\n prefix: 'motia:events',\n }),\n cronAdapter: appConfig.adapters?.cron || new RedisCronAdapter(redisClient),\n streamAdapter: appConfig.adapters?.streams || new RedisStreamAdapterManager(redisClient),\n }\n\n const lockedData = await generateLockedData({\n projectDir: baseDir,\n streamAdapter: adapters.streamAdapter,\n redisClient,\n streamAuth: appConfig.streamAuth,\n })\n\n const state = appConfig.adapters?.state || new RedisStateAdapter(redisClient)\n\n const config = { isVerbose }\n\n const motiaServer = createServer(lockedData, state, config, adapters, appConfig.app)\n const watcher = createDevWatchers(lockedData, motiaServer, motiaServer.motiaEventManager, motiaServer.cronManager)\n const plugins: MotiaPlugin[] = await processPlugins(motiaServer)\n\n // Initialize mermaid generator\n if (enableMermaid) {\n const mermaidGenerator = createMermaidGenerator(baseDir)\n mermaidGenerator.initialize(lockedData)\n trackEvent('mermaid_generator_initialized')\n }\n\n deployEndpoints(motiaServer, lockedData)\n\n motiaServer.app.get('/__motia', (_, res) => {\n const meta = {\n version,\n isDev: true,\n isTutorialDisabled,\n workbenchBase,\n }\n\n res //\n .header('Access-Control-Allow-Origin', '*')\n .header('Access-Control-Allow-Private-Network', 'true')\n .status(200)\n .json(meta)\n })\n\n trackEvent('dev_server_ready', {\n port,\n flows_count: lockedData.flows?.length || 0,\n steps_count: lockedData.activeSteps?.length || 0,\n flows: Object.keys(lockedData.flows || {}),\n steps: lockedData.activeSteps.map((step) => step.config.name),\n streams: Object.keys(lockedData.getStreams() || {}),\n runtime_version: version,\n environment: process.env.NODE_ENV || 'development',\n })\n\n const { applyMiddleware } = await import('@motiadev/workbench/middleware')\n\n await applyMiddleware({\n app: motiaServer.app,\n port,\n workbenchBase,\n plugins: plugins.flatMap((item) => item.workbench),\n })\n\n motiaServer.server.listen(port, hostname)\n console.log('🚀 Server ready and listening on port', port)\n console.log(`🔗 Open http://localhost:${port}${workbenchBase} to open workbench 🛠️`)\n\n process.on('SIGTERM', async () => {\n trackEvent('dev_server_shutdown', { reason: 'SIGTERM' })\n motiaServer.server.close()\n await watcher.stop()\n await stopRedisConnection()\n await flush().promise\n process.exit(0)\n })\n\n process.on('SIGINT', async () => {\n trackEvent('dev_server_shutdown', { reason: 'SIGINT' })\n motiaServer.server.close()\n await watcher.stop()\n await stopRedisConnection()\n await flush().promise\n process.exit(0)\n })\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"generate-locked-data.d.mts","names":[],"sources":["../src/generate-locked-data.ts"],"sourcesContent":[],"mappings":";;;;AAyDa,cAAA,YAmFZ,EAAA,CAAA,UAAA,EAAA,MAAA,EAAA,UAAA,EAnFkE,UAmFlE,EAAA,GAnF+E,OAmF/E,CAnFuF,IAmFvF,EAAA,CAAA;KAEI,iBAAA,GArF8D;EAAqB,YAAA,EAsFxE,gBAtFwE,CAAA,cAAA,CAAA;EAAR,aAAA,CAAA,EAuF9D,UAvF8D;CAAO;AAqFlF,cAKQ,kBAJG,EAAA,CAAA,MAAA,EAAA;EAIH,UAAA,EAAA,MAAA;EAEK,aAAA,CAAA,EAAA,oBAAA;EACF,WAAA,CAAA,EAAA,eAAA;EAED,WAAA,CAAA,EAAA,UAAA,GAAA,SAAA;EACH,UAAA,CAAA,EADG,iBACH;CAAR,EAAA,GAAA,OAAA,CAAQ,UAAR,CAAA"}
1
+ {"version":3,"file":"generate-locked-data.d.mts","names":[],"sources":["../src/generate-locked-data.ts"],"sourcesContent":[],"mappings":";;;;AAwDa,cAAA,YA4EZ,EAAA,CAAA,UAAA,EAAA,MAAA,EAAA,UAAA,EA5EkE,UA4ElE,EAAA,GA5E+E,OA4E/E,CA5EuF,IA4EvF,EAAA,CAAA;KAEI,iBAAA,GA9E8D;EAAqB,YAAA,EA+ExE,gBA/EwE,CAAA,cAAA,CAAA;EAAR,aAAA,CAAA,EAgF9D,UAhF8D;CAAO;AA8ElF,cAKQ,kBAJG,EAAA,CAAA,MAAA,EAAA;EAIH,UAAA,EAAA,MAAA;EAEK,aAAA,CAAA,EAAA,oBAAA;EACF,WAAA,CAAA,EAAA,eAAA;EAED,WAAA,CAAA,EAAA,UAAA,GAAA,SAAA;EACH,UAAA,CAAA,EADG,iBACH;CAAR,EAAA,GAAA,OAAA,CAAQ,UAAR,CAAA"}
@@ -1,4 +1,3 @@
1
- import { activatePythonVenv } from "./utils/activate-python-env.mjs";
2
1
  import { CompilationError } from "./utils/errors/compilation.error.mjs";
3
2
  import { LockedDataGenerationError } from "./utils/errors/locked-data-generation.error.mjs";
4
3
  import { LockedData, MemoryStreamAdapterManager, NoPrinter, Printer, getStepConfig, getStreamConfig } from "@motiadev/core";
@@ -53,7 +52,6 @@ const collectFlows = async (projectDir, lockedData) => {
53
52
  absolute: true,
54
53
  cwd: srcDir
55
54
  }) : []];
56
- if (stepFiles.some((file) => file.endsWith(".py")) || streamFiles.some((file) => file.endsWith(".py"))) activatePythonVenv({ baseDir: projectDir });
57
55
  for (const filePath of stepFiles) try {
58
56
  const config = await getStepConfig(filePath, projectDir);
59
57
  if (!config) {
@@ -1 +1 @@
1
- {"version":3,"file":"generate-locked-data.mjs","names":["invalidSteps: Step[]"],"sources":["../src/generate-locked-data.ts"],"sourcesContent":["import {\n getStepConfig,\n getStreamConfig,\n type JsonSchema,\n LockedData,\n MemoryStreamAdapterManager,\n NoPrinter,\n Printer,\n type Step,\n type StreamAdapterManager,\n type StreamAuthConfig,\n} from '@motiadev/core'\nimport { randomUUID } from 'crypto'\nimport { existsSync } from 'fs'\nimport { globSync } from 'glob'\nimport path from 'path'\nimport pc from 'picocolors'\nimport type { RedisClientType } from 'redis'\nimport { activatePythonVenv } from './utils/activate-python-env'\nimport { CompilationError } from './utils/errors/compilation.error'\nimport { LockedDataGenerationError } from './utils/errors/locked-data-generation.error'\n\nconst version = `${randomUUID()}:${Math.floor(Date.now() / 1000)}`\n\nconst getStepFilesFromDir = (dir: string): string[] => {\n if (!existsSync(dir)) {\n return []\n }\n return [\n ...globSync('**/*.step.{ts,js,rb}', { absolute: true, cwd: dir }),\n ...globSync('**/*_step.{ts,js,py,rb}', { absolute: true, cwd: dir }),\n ]\n}\n\nexport const getStepFiles = (projectDir: string): string[] => {\n const stepsDir = path.join(projectDir, 'steps')\n const srcDir = path.join(projectDir, 'src')\n return [...getStepFilesFromDir(stepsDir), ...getStepFilesFromDir(srcDir)]\n}\n\nconst getStreamFilesFromDir = (dir: string): string[] => {\n if (!existsSync(dir)) {\n return []\n }\n return [\n ...globSync('**/*.stream.{ts,js,rb}', { absolute: true, cwd: dir }),\n ...globSync('**/*_stream.{ts,js,py,rb}', { absolute: true, cwd: dir }),\n ]\n}\n\nexport const getStreamFiles = (projectDir: string): string[] => {\n const stepsDir = path.join(projectDir, 'steps')\n const srcDir = path.join(projectDir, 'src')\n return [...getStreamFilesFromDir(stepsDir), ...getStreamFilesFromDir(srcDir)]\n}\n\n// Helper function to recursively collect flow data\nexport const collectFlows = async (projectDir: string, lockedData: LockedData): Promise<Step[]> => {\n const invalidSteps: Step[] = []\n const stepFiles = getStepFiles(projectDir)\n const streamFiles = getStreamFiles(projectDir)\n const stepsDir = path.join(projectDir, 'steps')\n const srcDir = path.join(projectDir, 'src')\n const deprecatedSteps = [\n ...(existsSync(stepsDir) ? globSync('**/*.step.py', { absolute: true, cwd: stepsDir }) : []),\n ...(existsSync(srcDir) ? globSync('**/*.step.py', { absolute: true, cwd: srcDir }) : []),\n ]\n\n const hasPythonFiles =\n stepFiles.some((file) => file.endsWith('.py')) || streamFiles.some((file) => file.endsWith('.py'))\n\n if (hasPythonFiles) {\n activatePythonVenv({ baseDir: projectDir })\n }\n\n for (const filePath of stepFiles) {\n try {\n const config = await getStepConfig(filePath, projectDir)\n\n if (!config) {\n console.warn(`No config found in step ${filePath}, step skipped`)\n continue\n }\n\n const result = lockedData.createStep({ filePath, version, config }, { disableTypeCreation: true })\n\n if (!result) {\n invalidSteps.push({ filePath, version, config })\n }\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err)\n if (errorMessage.includes('Executable ruby not found') || errorMessage.includes('Executable python not found')) {\n console.warn(pc.yellow(`! [WARNING] Skipping step ${filePath}: ${errorMessage}`))\n continue\n }\n throw new CompilationError(`Error collecting flow ${filePath}`, path.relative(projectDir, filePath), err as Error)\n }\n }\n\n for (const filePath of streamFiles) {\n const config = await getStreamConfig(filePath)\n\n if (!config) {\n console.warn(`No config found in stream ${filePath}, stream skipped`)\n continue\n }\n\n lockedData.createStream({ filePath, config }, { disableTypeCreation: true })\n }\n\n if (deprecatedSteps.length > 0) {\n const warning = pc.yellow('! [WARNING]')\n console.warn(\n pc.yellow(\n [\n '',\n '========================================',\n warning,\n '',\n `Python steps with ${pc.gray('.step.py')} extensions are no longer supported.`,\n `Please rename them to ${pc.gray('_step.py')}.`,\n '',\n pc.bold('Steps:'),\n ...deprecatedSteps.map((step) =>\n pc.reset(\n `- ${pc.cyan(pc.bold(step.replace(projectDir, '')))} rename to ${pc.gray(`${step.replace(projectDir, '').replace('.step.py', '_step.py')}`)}`,\n ),\n ),\n\n '',\n 'Make sure the step names are importable from Python:',\n `- Don't use numbers, dots, dashes, commas, spaces, colons, or special characters`,\n '========================================',\n '',\n ].join('\\n'),\n ),\n )\n }\n\n return invalidSteps\n}\n\ntype StreamAuthOptions = {\n authenticate: StreamAuthConfig['authenticate']\n contextSchema?: JsonSchema\n}\n\nexport const generateLockedData = async (config: {\n projectDir: string\n streamAdapter?: StreamAdapterManager\n redisClient?: RedisClientType\n printerType?: 'disabled' | 'default'\n streamAuth?: StreamAuthOptions\n}): Promise<LockedData> => {\n try {\n const {\n projectDir,\n streamAdapter = new MemoryStreamAdapterManager(),\n printerType = 'default',\n redisClient,\n streamAuth,\n } = config\n const printer = printerType === 'disabled' ? new NoPrinter() : new Printer(projectDir)\n /*\n * NOTE: right now for performance and simplicity let's enforce a folder,\n * but we might want to remove this and scan the entire current directory\n */\n const lockedData = new LockedData(projectDir, streamAdapter, printer, redisClient)\n lockedData.setStreamAuthConfig(streamAuth)\n\n await collectFlows(projectDir, lockedData)\n lockedData.saveTypes()\n\n return lockedData\n } catch (error) {\n console.error(error)\n\n throw new LockedDataGenerationError(\n 'Failed to parse the project, generating locked data step failed',\n error as Error,\n )\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"generate-locked-data.mjs","names":["invalidSteps: Step[]"],"sources":["../src/generate-locked-data.ts"],"sourcesContent":["import {\n getStepConfig,\n getStreamConfig,\n type JsonSchema,\n LockedData,\n MemoryStreamAdapterManager,\n NoPrinter,\n Printer,\n type Step,\n type StreamAdapterManager,\n type StreamAuthConfig,\n} from '@motiadev/core'\nimport { randomUUID } from 'crypto'\nimport { existsSync } from 'fs'\nimport { globSync } from 'glob'\nimport path from 'path'\nimport pc from 'picocolors'\nimport type { RedisClientType } from 'redis'\nimport { CompilationError } from './utils/errors/compilation.error'\nimport { LockedDataGenerationError } from './utils/errors/locked-data-generation.error'\n\nconst version = `${randomUUID()}:${Math.floor(Date.now() / 1000)}`\n\nconst getStepFilesFromDir = (dir: string): string[] => {\n if (!existsSync(dir)) {\n return []\n }\n return [\n ...globSync('**/*.step.{ts,js,rb}', { absolute: true, cwd: dir }),\n ...globSync('**/*_step.{ts,js,py,rb}', { absolute: true, cwd: dir }),\n ]\n}\n\nexport const getStepFiles = (projectDir: string): string[] => {\n const stepsDir = path.join(projectDir, 'steps')\n const srcDir = path.join(projectDir, 'src')\n return [...getStepFilesFromDir(stepsDir), ...getStepFilesFromDir(srcDir)]\n}\n\nconst getStreamFilesFromDir = (dir: string): string[] => {\n if (!existsSync(dir)) {\n return []\n }\n return [\n ...globSync('**/*.stream.{ts,js,rb}', { absolute: true, cwd: dir }),\n ...globSync('**/*_stream.{ts,js,py,rb}', { absolute: true, cwd: dir }),\n ]\n}\n\nexport const getStreamFiles = (projectDir: string): string[] => {\n const stepsDir = path.join(projectDir, 'steps')\n const srcDir = path.join(projectDir, 'src')\n return [...getStreamFilesFromDir(stepsDir), ...getStreamFilesFromDir(srcDir)]\n}\n\n// Helper function to recursively collect flow data\nexport const collectFlows = async (projectDir: string, lockedData: LockedData): Promise<Step[]> => {\n const invalidSteps: Step[] = []\n const stepFiles = getStepFiles(projectDir)\n const streamFiles = getStreamFiles(projectDir)\n const stepsDir = path.join(projectDir, 'steps')\n const srcDir = path.join(projectDir, 'src')\n const deprecatedSteps = [\n ...(existsSync(stepsDir) ? globSync('**/*.step.py', { absolute: true, cwd: stepsDir }) : []),\n ...(existsSync(srcDir) ? globSync('**/*.step.py', { absolute: true, cwd: srcDir }) : []),\n ]\n\n for (const filePath of stepFiles) {\n try {\n const config = await getStepConfig(filePath, projectDir)\n\n if (!config) {\n console.warn(`No config found in step ${filePath}, step skipped`)\n continue\n }\n\n const result = lockedData.createStep({ filePath, version, config }, { disableTypeCreation: true })\n\n if (!result) {\n invalidSteps.push({ filePath, version, config })\n }\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err)\n if (errorMessage.includes('Executable ruby not found') || errorMessage.includes('Executable python not found')) {\n console.warn(pc.yellow(`! [WARNING] Skipping step ${filePath}: ${errorMessage}`))\n continue\n }\n throw new CompilationError(`Error collecting flow ${filePath}`, path.relative(projectDir, filePath), err as Error)\n }\n }\n\n for (const filePath of streamFiles) {\n const config = await getStreamConfig(filePath)\n\n if (!config) {\n console.warn(`No config found in stream ${filePath}, stream skipped`)\n continue\n }\n\n lockedData.createStream({ filePath, config }, { disableTypeCreation: true })\n }\n\n if (deprecatedSteps.length > 0) {\n const warning = pc.yellow('! [WARNING]')\n console.warn(\n pc.yellow(\n [\n '',\n '========================================',\n warning,\n '',\n `Python steps with ${pc.gray('.step.py')} extensions are no longer supported.`,\n `Please rename them to ${pc.gray('_step.py')}.`,\n '',\n pc.bold('Steps:'),\n ...deprecatedSteps.map((step) =>\n pc.reset(\n `- ${pc.cyan(pc.bold(step.replace(projectDir, '')))} rename to ${pc.gray(`${step.replace(projectDir, '').replace('.step.py', '_step.py')}`)}`,\n ),\n ),\n\n '',\n 'Make sure the step names are importable from Python:',\n `- Don't use numbers, dots, dashes, commas, spaces, colons, or special characters`,\n '========================================',\n '',\n ].join('\\n'),\n ),\n )\n }\n\n return invalidSteps\n}\n\ntype StreamAuthOptions = {\n authenticate: StreamAuthConfig['authenticate']\n contextSchema?: JsonSchema\n}\n\nexport const generateLockedData = async (config: {\n projectDir: string\n streamAdapter?: StreamAdapterManager\n redisClient?: RedisClientType\n printerType?: 'disabled' | 'default'\n streamAuth?: StreamAuthOptions\n}): Promise<LockedData> => {\n try {\n const {\n projectDir,\n streamAdapter = new MemoryStreamAdapterManager(),\n printerType = 'default',\n redisClient,\n streamAuth,\n } = config\n const printer = printerType === 'disabled' ? new NoPrinter() : new Printer(projectDir)\n /*\n * NOTE: right now for performance and simplicity let's enforce a folder,\n * but we might want to remove this and scan the entire current directory\n */\n const lockedData = new LockedData(projectDir, streamAdapter, printer, redisClient)\n lockedData.setStreamAuthConfig(streamAuth)\n\n await collectFlows(projectDir, lockedData)\n lockedData.saveTypes()\n\n return lockedData\n } catch (error) {\n console.error(error)\n\n throw new LockedDataGenerationError(\n 'Failed to parse the project, generating locked data step failed',\n error as Error,\n )\n }\n}\n"],"mappings":"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"}
package/dist/install.mjs CHANGED
@@ -1,7 +1,7 @@
1
+ import { getStepFiles, getStreamFiles } from "./generate-locked-data.mjs";
1
2
  import { executeCommand } from "./utils/execute-command.mjs";
2
3
  import { getPythonCommand } from "./utils/python-version-utils.mjs";
3
4
  import { activatePythonVenv } from "./utils/activate-python-env.mjs";
4
- import { getStepFiles, getStreamFiles } from "./generate-locked-data.mjs";
5
5
  import { ensureUvInstalled } from "./utils/ensure-uv.mjs";
6
6
  import { installLambdaPythonPackages } from "./utils/install-lambda-python-packages.mjs";
7
7
  import fs from "fs";
@@ -1,7 +1,7 @@
1
- import { executeCommand } from "../utils/execute-command.mjs";
2
1
  import { version } from "../version.mjs";
3
- import { pluginDependencies } from "./plugin-dependencies.mjs";
2
+ import { executeCommand } from "../utils/execute-command.mjs";
4
3
  import { getPackageManager } from "../utils/get-package-manager.mjs";
4
+ import { pluginDependencies } from "./plugin-dependencies.mjs";
5
5
  import fs from "node:fs";
6
6
  import path from "node:path";
7
7
 
@@ -38,7 +38,8 @@ const installPluginDependencies = async (baseDir, printer) => {
38
38
  const installCommand = {
39
39
  npm: "npm install",
40
40
  yarn: "yarn install",
41
- pnpm: "pnpm install"
41
+ pnpm: "pnpm install",
42
+ bun: "bun install"
42
43
  }[packageManager] || "npm install";
43
44
  try {
44
45
  await executeCommand(installCommand, baseDir, { silent: false });
@@ -1 +1 @@
1
- {"version":3,"file":"install-plugin-dependencies.mjs","names":["missingDependencies: string[]"],"sources":["../../src/plugins/install-plugin-dependencies.ts"],"sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport type { Printer } from '@motiadev/core'\nimport { executeCommand } from '../utils/execute-command'\nimport { getPackageManager } from '../utils/get-package-manager'\nimport { version } from '../version'\nimport { pluginDependencies } from './plugin-dependencies'\n\nexport const installPluginDependencies = async (baseDir: string, printer: Printer): Promise<void> => {\n const packageJsonPath = path.join(baseDir, 'package.json')\n\n if (!fs.existsSync(packageJsonPath)) {\n printer.printPluginWarn('No package.json found, skipping plugin dependency installation')\n return\n }\n\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))\n\n if (!packageJson.dependencies) {\n packageJson.dependencies = {}\n }\n\n const missingDependencies: string[] = []\n\n for (const dep of pluginDependencies) {\n if (packageJson.devDependencies?.[dep]) {\n delete packageJson.devDependencies[dep]\n }\n\n if (!packageJson.dependencies[dep]) {\n packageJson.dependencies[dep] = version\n missingDependencies.push(dep)\n }\n }\n\n if (missingDependencies.length === 0) {\n printer.printPluginLog('All plugin dependencies already installed')\n return\n }\n\n printer.printPluginLog(`Adding missing plugin dependencies: ${missingDependencies.join(', ')}`)\n\n fs.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\\n`)\n printer.printPluginLog('Updated package.json with plugin dependencies')\n\n let packageManager = getPackageManager(baseDir)\n if (packageManager === 'unknown') {\n printer.printPluginError('No package manager found, using npm as default')\n packageManager = 'npm'\n }\n printer.printPluginLog(`Installing dependencies using ${packageManager}...`)\n\n const installCommands: Record<string, string> = {\n npm: 'npm install',\n yarn: 'yarn install',\n pnpm: 'pnpm install',\n }\n\n const installCommand = installCommands[packageManager] || 'npm install'\n\n try {\n await executeCommand(installCommand, baseDir, { silent: false })\n printer.printPluginLog('Plugin dependencies installed successfully')\n } catch (error) {\n printer.printPluginError('Failed to install plugin dependencies:', error)\n printer.printPluginWarn(`Please run '${installCommand}' manually to install the dependencies`)\n }\n}\n"],"mappings":";;;;;;;;AAQA,MAAa,4BAA4B,OAAO,SAAiB,YAAoC;CACnG,MAAM,kBAAkB,KAAK,KAAK,SAAS,eAAe;AAE1D,KAAI,CAAC,GAAG,WAAW,gBAAgB,EAAE;AACnC,UAAQ,gBAAgB,iEAAiE;AACzF;;CAGF,MAAM,cAAc,KAAK,MAAM,GAAG,aAAa,iBAAiB,QAAQ,CAAC;AAEzE,KAAI,CAAC,YAAY,aACf,aAAY,eAAe,EAAE;CAG/B,MAAMA,sBAAgC,EAAE;AAExC,MAAK,MAAM,OAAO,oBAAoB;AACpC,MAAI,YAAY,kBAAkB,KAChC,QAAO,YAAY,gBAAgB;AAGrC,MAAI,CAAC,YAAY,aAAa,MAAM;AAClC,eAAY,aAAa,OAAO;AAChC,uBAAoB,KAAK,IAAI;;;AAIjC,KAAI,oBAAoB,WAAW,GAAG;AACpC,UAAQ,eAAe,4CAA4C;AACnE;;AAGF,SAAQ,eAAe,uCAAuC,oBAAoB,KAAK,KAAK,GAAG;AAE/F,IAAG,cAAc,iBAAiB,GAAG,KAAK,UAAU,aAAa,MAAM,EAAE,CAAC,IAAI;AAC9E,SAAQ,eAAe,gDAAgD;CAEvE,IAAI,iBAAiB,kBAAkB,QAAQ;AAC/C,KAAI,mBAAmB,WAAW;AAChC,UAAQ,iBAAiB,iDAAiD;AAC1E,mBAAiB;;AAEnB,SAAQ,eAAe,iCAAiC,eAAe,KAAK;CAQ5E,MAAM,iBAN0C;EAC9C,KAAK;EACL,MAAM;EACN,MAAM;EACP,CAEsC,mBAAmB;AAE1D,KAAI;AACF,QAAM,eAAe,gBAAgB,SAAS,EAAE,QAAQ,OAAO,CAAC;AAChE,UAAQ,eAAe,6CAA6C;UAC7D,OAAO;AACd,UAAQ,iBAAiB,0CAA0C,MAAM;AACzE,UAAQ,gBAAgB,eAAe,eAAe,wCAAwC"}
1
+ {"version":3,"file":"install-plugin-dependencies.mjs","names":["missingDependencies: string[]"],"sources":["../../src/plugins/install-plugin-dependencies.ts"],"sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport type { Printer } from '@motiadev/core'\nimport { executeCommand } from '../utils/execute-command'\nimport { getPackageManager } from '../utils/get-package-manager'\nimport { version } from '../version'\nimport { pluginDependencies } from './plugin-dependencies'\n\nexport const installPluginDependencies = async (baseDir: string, printer: Printer): Promise<void> => {\n const packageJsonPath = path.join(baseDir, 'package.json')\n\n if (!fs.existsSync(packageJsonPath)) {\n printer.printPluginWarn('No package.json found, skipping plugin dependency installation')\n return\n }\n\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))\n\n if (!packageJson.dependencies) {\n packageJson.dependencies = {}\n }\n\n const missingDependencies: string[] = []\n\n for (const dep of pluginDependencies) {\n if (packageJson.devDependencies?.[dep]) {\n delete packageJson.devDependencies[dep]\n }\n\n if (!packageJson.dependencies[dep]) {\n packageJson.dependencies[dep] = version\n missingDependencies.push(dep)\n }\n }\n\n if (missingDependencies.length === 0) {\n printer.printPluginLog('All plugin dependencies already installed')\n return\n }\n\n printer.printPluginLog(`Adding missing plugin dependencies: ${missingDependencies.join(', ')}`)\n\n fs.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\\n`)\n printer.printPluginLog('Updated package.json with plugin dependencies')\n\n let packageManager = getPackageManager(baseDir)\n if (packageManager === 'unknown') {\n printer.printPluginError('No package manager found, using npm as default')\n packageManager = 'npm'\n }\n printer.printPluginLog(`Installing dependencies using ${packageManager}...`)\n\n const installCommands: Record<string, string> = {\n npm: 'npm install',\n yarn: 'yarn install',\n pnpm: 'pnpm install',\n bun: 'bun install',\n }\n\n const installCommand = installCommands[packageManager] || 'npm install'\n\n try {\n await executeCommand(installCommand, baseDir, { silent: false })\n printer.printPluginLog('Plugin dependencies installed successfully')\n } catch (error) {\n printer.printPluginError('Failed to install plugin dependencies:', error)\n printer.printPluginWarn(`Please run '${installCommand}' manually to install the dependencies`)\n }\n}\n"],"mappings":";;;;;;;;AAQA,MAAa,4BAA4B,OAAO,SAAiB,YAAoC;CACnG,MAAM,kBAAkB,KAAK,KAAK,SAAS,eAAe;AAE1D,KAAI,CAAC,GAAG,WAAW,gBAAgB,EAAE;AACnC,UAAQ,gBAAgB,iEAAiE;AACzF;;CAGF,MAAM,cAAc,KAAK,MAAM,GAAG,aAAa,iBAAiB,QAAQ,CAAC;AAEzE,KAAI,CAAC,YAAY,aACf,aAAY,eAAe,EAAE;CAG/B,MAAMA,sBAAgC,EAAE;AAExC,MAAK,MAAM,OAAO,oBAAoB;AACpC,MAAI,YAAY,kBAAkB,KAChC,QAAO,YAAY,gBAAgB;AAGrC,MAAI,CAAC,YAAY,aAAa,MAAM;AAClC,eAAY,aAAa,OAAO;AAChC,uBAAoB,KAAK,IAAI;;;AAIjC,KAAI,oBAAoB,WAAW,GAAG;AACpC,UAAQ,eAAe,4CAA4C;AACnE;;AAGF,SAAQ,eAAe,uCAAuC,oBAAoB,KAAK,KAAK,GAAG;AAE/F,IAAG,cAAc,iBAAiB,GAAG,KAAK,UAAU,aAAa,MAAM,EAAE,CAAC,IAAI;AAC9E,SAAQ,eAAe,gDAAgD;CAEvE,IAAI,iBAAiB,kBAAkB,QAAQ;AAC/C,KAAI,mBAAmB,WAAW;AAChC,UAAQ,iBAAiB,iDAAiD;AAC1E,mBAAiB;;AAEnB,SAAQ,eAAe,iCAAiC,eAAe,KAAK;CAS5E,MAAM,iBAP0C;EAC9C,KAAK;EACL,MAAM;EACN,MAAM;EACN,KAAK;EACN,CAEsC,mBAAmB;AAE1D,KAAI;AACF,QAAM,eAAe,gBAAgB,SAAS,EAAE,QAAQ,OAAO,CAAC;AAChE,UAAQ,eAAe,6CAA6C;UAC7D,OAAO;AACd,UAAQ,iBAAiB,0CAA0C,MAAM;AACzE,UAAQ,gBAAgB,eAAe,eAAe,wCAAwC"}
package/dist/start.mjs CHANGED
@@ -1,6 +1,7 @@
1
- import { activatePythonVenv } from "./utils/activate-python-env.mjs";
2
1
  import { generateLockedData, getStepFiles, getStreamFiles } from "./generate-locked-data.mjs";
3
2
  import { version } from "./version.mjs";
3
+ import { activatePythonVenv } from "./utils/activate-python-env.mjs";
4
+ import { validatePythonEnvironment } from "./utils/validate-python-environment.mjs";
4
5
  import { loadMotiaConfig } from "./load-motia-config.mjs";
5
6
  import { workbenchBase } from "./constants.mjs";
6
7
  import { processPlugins } from "./plugins/process-plugins.mjs";
@@ -17,7 +18,12 @@ import { RedisStreamAdapterManager } from "@motiadev/adapter-redis-streams";
17
18
  const start = async (port, hostname, disableVerbose, motiaFileStorageDir) => {
18
19
  const baseDir = process.cwd();
19
20
  const isVerbose = !disableVerbose;
20
- if ([...getStepFiles(baseDir), ...getStreamFiles(baseDir)].some((file) => file.endsWith(".py"))) {
21
+ const pythonValidation = await validatePythonEnvironment({
22
+ baseDir,
23
+ hasPythonFiles: [...getStepFiles(baseDir), ...getStreamFiles(baseDir)].some((file) => file.endsWith(".py"))
24
+ });
25
+ if (!pythonValidation.success) process.exit(1);
26
+ if (pythonValidation.hasPythonFiles) {
21
27
  console.log("⚙️ Activating Python environment...");
22
28
  activatePythonVenv({
23
29
  baseDir,
@@ -1 +1 @@
1
- {"version":3,"file":"start.mjs","names":["redisClient: RedisClientType","plugins: MotiaPlugin[]"],"sources":["../src/start.ts"],"sourcesContent":["import { BullMQEventAdapter } from '@motiadev/adapter-bullmq-events'\nimport { RedisCronAdapter } from '@motiadev/adapter-redis-cron'\nimport { RedisStateAdapter } from '@motiadev/adapter-redis-state'\nimport { RedisStreamAdapterManager } from '@motiadev/adapter-redis-streams'\nimport { createServer, type MotiaPlugin } from '@motiadev/core'\nimport path from 'path'\nimport type { RedisClientType } from 'redis'\nimport { workbenchBase } from './constants'\nimport { generateLockedData, getStepFiles, getStreamFiles } from './generate-locked-data'\nimport { loadMotiaConfig } from './load-motia-config'\nimport { processPlugins } from './plugins/index'\nimport { getRedisClient, getRedisConnectionInfo, stopRedisConnection } from './redis/connection'\nimport { activatePythonVenv } from './utils/activate-python-env'\nimport { version } from './version'\n\nexport const start = async (\n port: number,\n hostname: string,\n disableVerbose: boolean,\n motiaFileStorageDir?: string,\n): Promise<void> => {\n const baseDir = process.cwd()\n const isVerbose = !disableVerbose\n\n const stepFiles = [...getStepFiles(baseDir), ...getStreamFiles(baseDir)]\n const hasPythonFiles = stepFiles.some((file) => file.endsWith('.py'))\n\n if (hasPythonFiles) {\n console.log('⚙️ Activating Python environment...')\n activatePythonVenv({ baseDir, isVerbose })\n }\n\n const motiaFileStoragePath = motiaFileStorageDir || '.motia'\n\n const dotMotia = path.join(baseDir, motiaFileStoragePath)\n const appConfig = await loadMotiaConfig(baseDir)\n\n const redisClient: RedisClientType = await getRedisClient(dotMotia, appConfig)\n\n const adapters = {\n eventAdapter:\n appConfig.adapters?.events ||\n new BullMQEventAdapter({\n connection: getRedisConnectionInfo(),\n }),\n cronAdapter: appConfig.adapters?.cron || new RedisCronAdapter(redisClient),\n streamAdapter: appConfig.adapters?.streams || new RedisStreamAdapterManager(redisClient),\n }\n const lockedData = await generateLockedData({\n projectDir: baseDir,\n streamAdapter: adapters.streamAdapter,\n redisClient,\n streamAuth: appConfig.streamAuth,\n })\n\n const state = appConfig.adapters?.state || new RedisStateAdapter(redisClient)\n\n const config = { isVerbose, isDev: false, version }\n\n const motiaServer = createServer(lockedData, state, config, adapters, appConfig.app)\n const plugins: MotiaPlugin[] = await processPlugins(motiaServer)\n\n if (!process.env.MOTIA_DOCKER_DISABLE_WORKBENCH) {\n const { applyMiddleware } = await import('@motiadev/workbench/middleware')\n await applyMiddleware({\n app: motiaServer.app,\n port,\n workbenchBase,\n plugins: plugins.flatMap((item) => item.workbench),\n })\n }\n\n motiaServer.server.listen(port, hostname)\n console.log('🚀 Server ready and listening on port', port)\n console.log(`🔗 Open http://${hostname}:${port}${workbenchBase} to open workbench 🛠️`)\n\n process.on('SIGTERM', async () => {\n motiaServer.server.close()\n await stopRedisConnection()\n process.exit(0)\n })\n\n process.on('SIGINT', async () => {\n motiaServer.server.close()\n await stopRedisConnection()\n process.exit(0)\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAeA,MAAa,QAAQ,OACnB,MACA,UACA,gBACA,wBACkB;CAClB,MAAM,UAAU,QAAQ,KAAK;CAC7B,MAAM,YAAY,CAAC;AAKnB,KAHkB,CAAC,GAAG,aAAa,QAAQ,EAAE,GAAG,eAAe,QAAQ,CAAC,CACvC,MAAM,SAAS,KAAK,SAAS,MAAM,CAAC,EAEjD;AAClB,UAAQ,IAAI,sCAAsC;AAClD,qBAAmB;GAAE;GAAS;GAAW,CAAC;;CAG5C,MAAM,uBAAuB,uBAAuB;CAEpD,MAAM,WAAW,KAAK,KAAK,SAAS,qBAAqB;CACzD,MAAM,YAAY,MAAM,gBAAgB,QAAQ;CAEhD,MAAMA,cAA+B,MAAM,eAAe,UAAU,UAAU;CAE9E,MAAM,WAAW;EACf,cACE,UAAU,UAAU,UACpB,IAAI,mBAAmB,EACrB,YAAY,wBAAwB,EACrC,CAAC;EACJ,aAAa,UAAU,UAAU,QAAQ,IAAI,iBAAiB,YAAY;EAC1E,eAAe,UAAU,UAAU,WAAW,IAAI,0BAA0B,YAAY;EACzF;CAYD,MAAM,cAAc,aAXD,MAAM,mBAAmB;EAC1C,YAAY;EACZ,eAAe,SAAS;EACxB;EACA,YAAY,UAAU;EACvB,CAAC,EAEY,UAAU,UAAU,SAAS,IAAI,kBAAkB,YAAY,EAE9D;EAAE;EAAW,OAAO;EAAO;EAAS,EAES,UAAU,UAAU,IAAI;CACpF,MAAMC,UAAyB,MAAM,eAAe,YAAY;AAEhE,KAAI,CAAC,QAAQ,IAAI,gCAAgC;EAC/C,MAAM,EAAE,oBAAoB,MAAM,OAAO;AACzC,QAAM,gBAAgB;GACpB,KAAK,YAAY;GACjB;GACA;GACA,SAAS,QAAQ,SAAS,SAAS,KAAK,UAAU;GACnD,CAAC;;AAGJ,aAAY,OAAO,OAAO,MAAM,SAAS;AACzC,SAAQ,IAAI,yCAAyC,KAAK;AAC1D,SAAQ,IAAI,kBAAkB,SAAS,GAAG,OAAO,cAAc,wBAAwB;AAEvF,SAAQ,GAAG,WAAW,YAAY;AAChC,cAAY,OAAO,OAAO;AAC1B,QAAM,qBAAqB;AAC3B,UAAQ,KAAK,EAAE;GACf;AAEF,SAAQ,GAAG,UAAU,YAAY;AAC/B,cAAY,OAAO,OAAO;AAC1B,QAAM,qBAAqB;AAC3B,UAAQ,KAAK,EAAE;GACf"}
1
+ {"version":3,"file":"start.mjs","names":["redisClient: RedisClientType","plugins: MotiaPlugin[]"],"sources":["../src/start.ts"],"sourcesContent":["import { BullMQEventAdapter } from '@motiadev/adapter-bullmq-events'\nimport { RedisCronAdapter } from '@motiadev/adapter-redis-cron'\nimport { RedisStateAdapter } from '@motiadev/adapter-redis-state'\nimport { RedisStreamAdapterManager } from '@motiadev/adapter-redis-streams'\nimport { createServer, type MotiaPlugin } from '@motiadev/core'\nimport path from 'path'\nimport type { RedisClientType } from 'redis'\nimport { workbenchBase } from './constants'\nimport { generateLockedData, getStepFiles, getStreamFiles } from './generate-locked-data'\nimport { loadMotiaConfig } from './load-motia-config'\nimport { processPlugins } from './plugins/index'\nimport { getRedisClient, getRedisConnectionInfo, stopRedisConnection } from './redis/connection'\nimport { activatePythonVenv } from './utils/activate-python-env'\nimport { validatePythonEnvironment } from './utils/validate-python-environment'\nimport { version } from './version'\n\nexport const start = async (\n port: number,\n hostname: string,\n disableVerbose: boolean,\n motiaFileStorageDir?: string,\n): Promise<void> => {\n const baseDir = process.cwd()\n const isVerbose = !disableVerbose\n\n const stepFiles = [...getStepFiles(baseDir), ...getStreamFiles(baseDir)]\n const hasPythonFiles = stepFiles.some((file) => file.endsWith('.py'))\n\n const pythonValidation = await validatePythonEnvironment({ baseDir, hasPythonFiles })\n if (!pythonValidation.success) {\n process.exit(1)\n }\n\n if (pythonValidation.hasPythonFiles) {\n console.log('⚙️ Activating Python environment...')\n activatePythonVenv({ baseDir, isVerbose })\n }\n\n const motiaFileStoragePath = motiaFileStorageDir || '.motia'\n\n const dotMotia = path.join(baseDir, motiaFileStoragePath)\n const appConfig = await loadMotiaConfig(baseDir)\n\n const redisClient: RedisClientType = await getRedisClient(dotMotia, appConfig)\n\n const adapters = {\n eventAdapter:\n appConfig.adapters?.events ||\n new BullMQEventAdapter({\n connection: getRedisConnectionInfo(),\n }),\n cronAdapter: appConfig.adapters?.cron || new RedisCronAdapter(redisClient),\n streamAdapter: appConfig.adapters?.streams || new RedisStreamAdapterManager(redisClient),\n }\n const lockedData = await generateLockedData({\n projectDir: baseDir,\n streamAdapter: adapters.streamAdapter,\n redisClient,\n streamAuth: appConfig.streamAuth,\n })\n\n const state = appConfig.adapters?.state || new RedisStateAdapter(redisClient)\n\n const config = { isVerbose, isDev: false, version }\n\n const motiaServer = createServer(lockedData, state, config, adapters, appConfig.app)\n const plugins: MotiaPlugin[] = await processPlugins(motiaServer)\n\n if (!process.env.MOTIA_DOCKER_DISABLE_WORKBENCH) {\n const { applyMiddleware } = await import('@motiadev/workbench/middleware')\n await applyMiddleware({\n app: motiaServer.app,\n port,\n workbenchBase,\n plugins: plugins.flatMap((item) => item.workbench),\n })\n }\n\n motiaServer.server.listen(port, hostname)\n console.log('🚀 Server ready and listening on port', port)\n console.log(`🔗 Open http://${hostname}:${port}${workbenchBase} to open workbench 🛠️`)\n\n process.on('SIGTERM', async () => {\n motiaServer.server.close()\n await stopRedisConnection()\n process.exit(0)\n })\n\n process.on('SIGINT', async () => {\n motiaServer.server.close()\n await stopRedisConnection()\n process.exit(0)\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAgBA,MAAa,QAAQ,OACnB,MACA,UACA,gBACA,wBACkB;CAClB,MAAM,UAAU,QAAQ,KAAK;CAC7B,MAAM,YAAY,CAAC;CAKnB,MAAM,mBAAmB,MAAM,0BAA0B;EAAE;EAAS,gBAHlD,CAAC,GAAG,aAAa,QAAQ,EAAE,GAAG,eAAe,QAAQ,CAAC,CACvC,MAAM,SAAS,KAAK,SAAS,MAAM,CAAC;EAEe,CAAC;AACrF,KAAI,CAAC,iBAAiB,QACpB,SAAQ,KAAK,EAAE;AAGjB,KAAI,iBAAiB,gBAAgB;AACnC,UAAQ,IAAI,sCAAsC;AAClD,qBAAmB;GAAE;GAAS;GAAW,CAAC;;CAG5C,MAAM,uBAAuB,uBAAuB;CAEpD,MAAM,WAAW,KAAK,KAAK,SAAS,qBAAqB;CACzD,MAAM,YAAY,MAAM,gBAAgB,QAAQ;CAEhD,MAAMA,cAA+B,MAAM,eAAe,UAAU,UAAU;CAE9E,MAAM,WAAW;EACf,cACE,UAAU,UAAU,UACpB,IAAI,mBAAmB,EACrB,YAAY,wBAAwB,EACrC,CAAC;EACJ,aAAa,UAAU,UAAU,QAAQ,IAAI,iBAAiB,YAAY;EAC1E,eAAe,UAAU,UAAU,WAAW,IAAI,0BAA0B,YAAY;EACzF;CAYD,MAAM,cAAc,aAXD,MAAM,mBAAmB;EAC1C,YAAY;EACZ,eAAe,SAAS;EACxB;EACA,YAAY,UAAU;EACvB,CAAC,EAEY,UAAU,UAAU,SAAS,IAAI,kBAAkB,YAAY,EAE9D;EAAE;EAAW,OAAO;EAAO;EAAS,EAES,UAAU,UAAU,IAAI;CACpF,MAAMC,UAAyB,MAAM,eAAe,YAAY;AAEhE,KAAI,CAAC,QAAQ,IAAI,gCAAgC;EAC/C,MAAM,EAAE,oBAAoB,MAAM,OAAO;AACzC,QAAM,gBAAgB;GACpB,KAAK,YAAY;GACjB;GACA;GACA,SAAS,QAAQ,SAAS,SAAS,KAAK,UAAU;GACnD,CAAC;;AAGJ,aAAY,OAAO,OAAO,MAAM,SAAS;AACzC,SAAQ,IAAI,yCAAyC,KAAK;AAC1D,SAAQ,IAAI,kBAAkB,SAAS,GAAG,OAAO,cAAc,wBAAwB;AAEvF,SAAQ,GAAG,WAAW,YAAY;AAChC,cAAY,OAAO,OAAO;AAC1B,QAAM,qBAAqB;AAC3B,UAAQ,KAAK,EAAE;GACf;AAEF,SAAQ,GAAG,UAAU,YAAY;AAC/B,cAAY,OAAO,OAAO;AAC1B,QAAM,qBAAqB;AAC3B,UAAQ,KAAK,EAAE;GACf"}
@@ -1,6 +1,5 @@
1
1
  import { internalLogger } from "./internal-logger.mjs";
2
2
  import { findPythonSitePackagesDir } from "./python-version-utils.mjs";
3
- import fs from "fs";
4
3
  import path from "path";
5
4
 
6
5
  //#region src/utils/activate-python-env.ts
@@ -17,20 +16,15 @@ const activatePythonVenv = ({ baseDir, isVerbose = false, pythonVersion = "3.13"
17
16
  baseDir,
18
17
  pythonVersion
19
18
  });
20
- if (fs.existsSync(venvPath)) {
21
- process.env.PATH = `${venvBinPath}${path.delimiter}${process.env.PATH}`;
22
- process.env.VIRTUAL_ENV = venvPath;
23
- process.env.PYTHON_SITE_PACKAGES = sitePackagesPath;
24
- delete process.env.PYTHONHOME;
25
- if (isVerbose) {
26
- const pythonPath = process.platform === "win32" ? path.join(venvBinPath, "python.exe") : path.join(venvBinPath, "python");
27
- const relativePath = (path$1) => path$1.replace(baseDir, "<projectDir>");
28
- internalLogger.info("Using Python", relativePath(pythonPath));
29
- internalLogger.info("Site-packages path", relativePath(sitePackagesPath));
30
- }
31
- } else {
32
- internalLogger.error("Python virtual environment not found in python_modules/");
33
- internalLogger.error("Please run `motia install` to create a new virtual environment");
19
+ process.env.PATH = `${venvBinPath}${path.delimiter}${process.env.PATH}`;
20
+ process.env.VIRTUAL_ENV = venvPath;
21
+ process.env.PYTHON_SITE_PACKAGES = sitePackagesPath;
22
+ delete process.env.PYTHONHOME;
23
+ if (isVerbose) {
24
+ const pythonPath = process.platform === "win32" ? path.join(venvBinPath, "python.exe") : path.join(venvBinPath, "python");
25
+ const relativePath = (path$1) => path$1.replace(baseDir, "<projectDir>");
26
+ internalLogger.info("Using Python", relativePath(pythonPath));
27
+ internalLogger.info("Site-packages path", relativePath(sitePackagesPath));
34
28
  }
35
29
  };
36
30
 
@@ -1 +1 @@
1
- {"version":3,"file":"activate-python-env.mjs","names":["path"],"sources":["../../src/utils/activate-python-env.ts"],"sourcesContent":["import fs from 'fs'\nimport path from 'path'\nimport { internalLogger } from './internal-logger'\nimport { findPythonSitePackagesDir } from './python-version-utils'\n\ninterface VenvConfig {\n baseDir: string\n isVerbose?: boolean\n pythonVersion?: string\n}\n\nexport const getSitePackagesPath = ({ baseDir, pythonVersion = '3.13' }: VenvConfig): string => {\n const venvPath = path.join(baseDir, 'python_modules')\n const libPath = path.join(venvPath, 'lib')\n const actualPythonVersionPath = findPythonSitePackagesDir(libPath, pythonVersion)\n return path.join(venvPath, 'lib', actualPythonVersionPath, 'site-packages')\n}\n\nexport const activatePythonVenv = ({ baseDir, isVerbose = false, pythonVersion = '3.13' }: VenvConfig): void => {\n internalLogger.info('Activating Python environment')\n\n // Set the virtual environment path\n const venvPath = path.join(baseDir, 'python_modules')\n const venvBinPath = path.join(venvPath, process.platform === 'win32' ? 'Scripts' : 'bin')\n\n // Find the Python version directory using the utility function\n const sitePackagesPath = getSitePackagesPath({ baseDir, pythonVersion })\n\n // Verify that the virtual environment exists\n if (fs.existsSync(venvPath)) {\n // Add virtual environment to PATH\n process.env.PATH = `${venvBinPath}${path.delimiter}${process.env.PATH}`\n // Set VIRTUAL_ENV environment variable\n process.env.VIRTUAL_ENV = venvPath\n // Set PYTHON_SITE_PACKAGES with the site-packages path\n process.env.PYTHON_SITE_PACKAGES = sitePackagesPath\n // Remove PYTHONHOME if it exists as it can interfere with venv\n delete process.env.PYTHONHOME\n\n // Log Python environment information if verbose mode is enabled\n if (isVerbose) {\n const pythonPath =\n process.platform === 'win32' ? path.join(venvBinPath, 'python.exe') : path.join(venvBinPath, 'python')\n\n const relativePath = (path: string) => path.replace(baseDir, '<projectDir>')\n\n internalLogger.info('Using Python', relativePath(pythonPath))\n internalLogger.info('Site-packages path', relativePath(sitePackagesPath))\n }\n } else {\n internalLogger.error('Python virtual environment not found in python_modules/')\n internalLogger.error('Please run `motia install` to create a new virtual environment')\n }\n}\n"],"mappings":";;;;;;AAWA,MAAa,uBAAuB,EAAE,SAAS,gBAAgB,aAAiC;CAC9F,MAAM,WAAW,KAAK,KAAK,SAAS,iBAAiB;CAErD,MAAM,0BAA0B,0BADhB,KAAK,KAAK,UAAU,MAAM,EACyB,cAAc;AACjF,QAAO,KAAK,KAAK,UAAU,OAAO,yBAAyB,gBAAgB;;AAG7E,MAAa,sBAAsB,EAAE,SAAS,YAAY,OAAO,gBAAgB,aAA+B;AAC9G,gBAAe,KAAK,gCAAgC;CAGpD,MAAM,WAAW,KAAK,KAAK,SAAS,iBAAiB;CACrD,MAAM,cAAc,KAAK,KAAK,UAAU,QAAQ,aAAa,UAAU,YAAY,MAAM;CAGzF,MAAM,mBAAmB,oBAAoB;EAAE;EAAS;EAAe,CAAC;AAGxE,KAAI,GAAG,WAAW,SAAS,EAAE;AAE3B,UAAQ,IAAI,OAAO,GAAG,cAAc,KAAK,YAAY,QAAQ,IAAI;AAEjE,UAAQ,IAAI,cAAc;AAE1B,UAAQ,IAAI,uBAAuB;AAEnC,SAAO,QAAQ,IAAI;AAGnB,MAAI,WAAW;GACb,MAAM,aACJ,QAAQ,aAAa,UAAU,KAAK,KAAK,aAAa,aAAa,GAAG,KAAK,KAAK,aAAa,SAAS;GAExG,MAAM,gBAAgB,WAAiBA,OAAK,QAAQ,SAAS,eAAe;AAE5E,kBAAe,KAAK,gBAAgB,aAAa,WAAW,CAAC;AAC7D,kBAAe,KAAK,sBAAsB,aAAa,iBAAiB,CAAC;;QAEtE;AACL,iBAAe,MAAM,0DAA0D;AAC/E,iBAAe,MAAM,iEAAiE"}
1
+ {"version":3,"file":"activate-python-env.mjs","names":["path"],"sources":["../../src/utils/activate-python-env.ts"],"sourcesContent":["import fs from 'fs'\nimport path from 'path'\nimport { internalLogger } from './internal-logger'\nimport { findPythonSitePackagesDir } from './python-version-utils'\n\ninterface VenvConfig {\n baseDir: string\n isVerbose?: boolean\n pythonVersion?: string\n}\n\nexport const getSitePackagesPath = ({ baseDir, pythonVersion = '3.13' }: VenvConfig): string => {\n const venvPath = path.join(baseDir, 'python_modules')\n const libPath = path.join(venvPath, 'lib')\n const actualPythonVersionPath = findPythonSitePackagesDir(libPath, pythonVersion)\n return path.join(venvPath, 'lib', actualPythonVersionPath, 'site-packages')\n}\n\nexport const activatePythonVenv = ({ baseDir, isVerbose = false, pythonVersion = '3.13' }: VenvConfig): void => {\n internalLogger.info('Activating Python environment')\n\n // Set the virtual environment path\n const venvPath = path.join(baseDir, 'python_modules')\n const venvBinPath = path.join(venvPath, process.platform === 'win32' ? 'Scripts' : 'bin')\n\n // Find the Python version directory using the utility function\n const sitePackagesPath = getSitePackagesPath({ baseDir, pythonVersion })\n\n // Add virtual environment to PATH\n process.env.PATH = `${venvBinPath}${path.delimiter}${process.env.PATH}`\n // Set VIRTUAL_ENV environment variable\n process.env.VIRTUAL_ENV = venvPath\n // Set PYTHON_SITE_PACKAGES with the site-packages path\n process.env.PYTHON_SITE_PACKAGES = sitePackagesPath\n // Remove PYTHONHOME if it exists as it can interfere with venv\n delete process.env.PYTHONHOME\n\n // Log Python environment information if verbose mode is enabled\n if (isVerbose) {\n const pythonPath =\n process.platform === 'win32' ? path.join(venvBinPath, 'python.exe') : path.join(venvBinPath, 'python')\n\n const relativePath = (path: string) => path.replace(baseDir, '<projectDir>')\n\n internalLogger.info('Using Python', relativePath(pythonPath))\n internalLogger.info('Site-packages path', relativePath(sitePackagesPath))\n }\n}\n"],"mappings":";;;;;AAWA,MAAa,uBAAuB,EAAE,SAAS,gBAAgB,aAAiC;CAC9F,MAAM,WAAW,KAAK,KAAK,SAAS,iBAAiB;CAErD,MAAM,0BAA0B,0BADhB,KAAK,KAAK,UAAU,MAAM,EACyB,cAAc;AACjF,QAAO,KAAK,KAAK,UAAU,OAAO,yBAAyB,gBAAgB;;AAG7E,MAAa,sBAAsB,EAAE,SAAS,YAAY,OAAO,gBAAgB,aAA+B;AAC9G,gBAAe,KAAK,gCAAgC;CAGpD,MAAM,WAAW,KAAK,KAAK,SAAS,iBAAiB;CACrD,MAAM,cAAc,KAAK,KAAK,UAAU,QAAQ,aAAa,UAAU,YAAY,MAAM;CAGzF,MAAM,mBAAmB,oBAAoB;EAAE;EAAS;EAAe,CAAC;AAGxE,SAAQ,IAAI,OAAO,GAAG,cAAc,KAAK,YAAY,QAAQ,IAAI;AAEjE,SAAQ,IAAI,cAAc;AAE1B,SAAQ,IAAI,uBAAuB;AAEnC,QAAO,QAAQ,IAAI;AAGnB,KAAI,WAAW;EACb,MAAM,aACJ,QAAQ,aAAa,UAAU,KAAK,KAAK,aAAa,aAAa,GAAG,KAAK,KAAK,aAAa,SAAS;EAExG,MAAM,gBAAgB,WAAiBA,OAAK,QAAQ,SAAS,eAAe;AAE5E,iBAAe,KAAK,gBAAgB,aAAa,WAAW,CAAC;AAC7D,iBAAe,KAAK,sBAAsB,aAAa,iBAAiB,CAAC"}
@@ -1,13 +1,50 @@
1
1
  import { checkIfFileExists } from "../create/utils.mjs";
2
+ import fs from "fs";
3
+ import path from "path";
2
4
 
3
5
  //#region src/utils/get-package-manager.ts
6
+ const getPackageManagerFromEnv = () => {
7
+ const userAgent = process.env.npm_config_user_agent;
8
+ if (!userAgent) return null;
9
+ const match = userAgent.match(/^(npm|pnpm|yarn|bun)\//);
10
+ if (match) return match[1];
11
+ return null;
12
+ };
13
+ const readPackageManagerFromPackageJson = (dir) => {
14
+ const packageJsonPath = path.join(dir, "package.json");
15
+ if (!checkIfFileExists(dir, "package.json")) return null;
16
+ try {
17
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
18
+ if (packageJson.packageManager) {
19
+ const pm = packageJson.packageManager.split("@")[0];
20
+ if ([
21
+ "npm",
22
+ "yarn",
23
+ "pnpm",
24
+ "bun"
25
+ ].includes(pm)) return pm;
26
+ }
27
+ } catch {
28
+ return null;
29
+ }
30
+ return null;
31
+ };
4
32
  const getPackageManager = (dir) => {
5
- if (checkIfFileExists(dir, "yarn.lock")) return "yarn";
6
- else if (checkIfFileExists(dir, "pnpm-lock.yaml")) return "pnpm";
7
- else if (checkIfFileExists(dir, "package-lock.json")) return "npm";
8
- else return "unknown";
33
+ const envPackageManager = getPackageManagerFromEnv();
34
+ if (envPackageManager) return envPackageManager;
35
+ let currentDir = dir;
36
+ while (currentDir !== path.dirname(currentDir)) {
37
+ if (checkIfFileExists(currentDir, "yarn.lock")) return "yarn";
38
+ else if (checkIfFileExists(currentDir, "pnpm-lock.yaml")) return "pnpm";
39
+ else if (checkIfFileExists(currentDir, "package-lock.json")) return "npm";
40
+ else if (checkIfFileExists(currentDir, "bun.lockb") || checkIfFileExists(currentDir, "bun.lock")) return "bun";
41
+ const packageManagerFromJson = readPackageManagerFromPackageJson(currentDir);
42
+ if (packageManagerFromJson) return packageManagerFromJson;
43
+ currentDir = path.dirname(currentDir);
44
+ }
45
+ return "npm";
9
46
  };
10
47
 
11
48
  //#endregion
12
- export { getPackageManager };
49
+ export { getPackageManager, getPackageManagerFromEnv };
13
50
  //# sourceMappingURL=get-package-manager.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"get-package-manager.mjs","names":[],"sources":["../../src/utils/get-package-manager.ts"],"sourcesContent":["import { checkIfFileExists } from '../create/utils'\n\nexport const getPackageManager = (dir: string): string => {\n if (checkIfFileExists(dir, 'yarn.lock')) {\n return 'yarn'\n } else if (checkIfFileExists(dir, 'pnpm-lock.yaml')) {\n return 'pnpm'\n } else if (checkIfFileExists(dir, 'package-lock.json')) {\n return 'npm'\n } else {\n return 'unknown'\n }\n}\n"],"mappings":";;;AAEA,MAAa,qBAAqB,QAAwB;AACxD,KAAI,kBAAkB,KAAK,YAAY,CACrC,QAAO;UACE,kBAAkB,KAAK,iBAAiB,CACjD,QAAO;UACE,kBAAkB,KAAK,oBAAoB,CACpD,QAAO;KAEP,QAAO"}
1
+ {"version":3,"file":"get-package-manager.mjs","names":[],"sources":["../../src/utils/get-package-manager.ts"],"sourcesContent":["import fs from 'fs'\nimport path from 'path'\nimport { checkIfFileExists } from '../create/utils'\n\nexport const getPackageManagerFromEnv = (): string | null => {\n const userAgent = process.env.npm_config_user_agent\n if (!userAgent) {\n return null\n }\n\n const match = userAgent.match(/^(npm|pnpm|yarn|bun)\\//)\n if (match) {\n return match[1]\n }\n\n return null\n}\n\nconst readPackageManagerFromPackageJson = (dir: string): string | null => {\n const packageJsonPath = path.join(dir, 'package.json')\n if (!checkIfFileExists(dir, 'package.json')) {\n return null\n }\n\n try {\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))\n if (packageJson.packageManager) {\n const pm = packageJson.packageManager.split('@')[0]\n if (['npm', 'yarn', 'pnpm', 'bun'].includes(pm)) {\n return pm\n }\n }\n } catch {\n return null\n }\n\n return null\n}\n\nexport const getPackageManager = (dir: string): string => {\n const envPackageManager = getPackageManagerFromEnv()\n if (envPackageManager) {\n return envPackageManager\n }\n\n let currentDir = dir\n\n while (currentDir !== path.dirname(currentDir)) {\n if (checkIfFileExists(currentDir, 'yarn.lock')) {\n return 'yarn'\n } else if (checkIfFileExists(currentDir, 'pnpm-lock.yaml')) {\n return 'pnpm'\n } else if (checkIfFileExists(currentDir, 'package-lock.json')) {\n return 'npm'\n } else if (checkIfFileExists(currentDir, 'bun.lockb') || checkIfFileExists(currentDir, 'bun.lock')) {\n return 'bun'\n }\n\n const packageManagerFromJson = readPackageManagerFromPackageJson(currentDir)\n if (packageManagerFromJson) {\n return packageManagerFromJson\n }\n\n currentDir = path.dirname(currentDir)\n }\n\n return 'npm'\n}\n"],"mappings":";;;;;AAIA,MAAa,iCAAgD;CAC3D,MAAM,YAAY,QAAQ,IAAI;AAC9B,KAAI,CAAC,UACH,QAAO;CAGT,MAAM,QAAQ,UAAU,MAAM,yBAAyB;AACvD,KAAI,MACF,QAAO,MAAM;AAGf,QAAO;;AAGT,MAAM,qCAAqC,QAA+B;CACxE,MAAM,kBAAkB,KAAK,KAAK,KAAK,eAAe;AACtD,KAAI,CAAC,kBAAkB,KAAK,eAAe,CACzC,QAAO;AAGT,KAAI;EACF,MAAM,cAAc,KAAK,MAAM,GAAG,aAAa,iBAAiB,QAAQ,CAAC;AACzE,MAAI,YAAY,gBAAgB;GAC9B,MAAM,KAAK,YAAY,eAAe,MAAM,IAAI,CAAC;AACjD,OAAI;IAAC;IAAO;IAAQ;IAAQ;IAAM,CAAC,SAAS,GAAG,CAC7C,QAAO;;SAGL;AACN,SAAO;;AAGT,QAAO;;AAGT,MAAa,qBAAqB,QAAwB;CACxD,MAAM,oBAAoB,0BAA0B;AACpD,KAAI,kBACF,QAAO;CAGT,IAAI,aAAa;AAEjB,QAAO,eAAe,KAAK,QAAQ,WAAW,EAAE;AAC9C,MAAI,kBAAkB,YAAY,YAAY,CAC5C,QAAO;WACE,kBAAkB,YAAY,iBAAiB,CACxD,QAAO;WACE,kBAAkB,YAAY,oBAAoB,CAC3D,QAAO;WACE,kBAAkB,YAAY,YAAY,IAAI,kBAAkB,YAAY,WAAW,CAChG,QAAO;EAGT,MAAM,yBAAyB,kCAAkC,WAAW;AAC5E,MAAI,uBACF,QAAO;AAGT,eAAa,KAAK,QAAQ,WAAW;;AAGvC,QAAO"}