motia 0.15.4-beta.170-590272 → 0.15.5-beta.171

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.
@@ -1,5 +1,4 @@
1
1
  import { BuildError, BuildErrorType } from "../../utils/errors/build.error.mjs";
2
- import { validatePythonEnvironment } from "../../utils/validate-python-environment.mjs";
3
2
  import { collectFlows, getStepFiles, getStreamFiles } from "../../generate-locked-data.mjs";
4
3
  import { Builder } from "../build/builder.mjs";
5
4
  import { distDir, projectDir, stepsConfigPath } from "./constants.mjs";
@@ -24,12 +23,7 @@ const build = async (listener) => {
24
23
  });
25
24
  fs.mkdirSync(distDir, { recursive: true });
26
25
  const lockedData = new LockedData(projectDir, new MemoryStreamAdapterManager(), new NoPrinter());
27
- const hasPython = hasPythonSteps([...stepFiles, ...streamFiles]);
28
- if (!(await validatePythonEnvironment({
29
- baseDir: projectDir,
30
- hasPythonFiles: hasPython
31
- })).success) throw new BuildError(BuildErrorType.COMPILATION, void 0, "Python environment validation failed. Please run the install command to set up your Python environment.");
32
- if (hasPython) builder.registerBuilder("python", new PythonBuilder(builder, listener));
26
+ if (hasPythonSteps([...stepFiles, ...streamFiles])) builder.registerBuilder("python", new PythonBuilder(builder, listener));
33
27
  if ((await collectFlows(projectDir, lockedData).catch((err) => {
34
28
  const finalMessage = `${err.filePath ? `Build error in ${err.filePath}` : "Build error"}\nPlease check the logs above for details`;
35
29
  throw new BuildError(BuildErrorType.COMPILATION, err.filePath, finalMessage, err);
@@ -1 +1 @@
1
- {"version":3,"file":"build.mjs","names":["stepsFile: StepsConfigFile"],"sources":["../../../src/cloud/new-deployment/build.ts"],"sourcesContent":["import { isApiStep, LockedData, MemoryStreamAdapterManager, NoPrinter } from '@motiadev/core'\nimport fs from 'fs'\nimport { collectFlows, getStepFiles, getStreamFiles } from '../../generate-locked-data'\nimport { BuildError, BuildErrorType } from '../../utils/errors/build.error'\nimport { validatePythonEnvironment } from '../../utils/validate-python-environment'\nimport { Builder, type StepsConfigFile } from '../build/builder'\nimport { NodeBuilder } from '../build/builders/node/index'\nimport { PythonBuilder } from '../build/builders/python/index'\nimport { distDir, projectDir, stepsConfigPath } from './constants'\nimport type { BuildListener } from './listeners/listener.types'\n\nconst hasPythonSteps = (stepFiles: string[]) => {\n return stepFiles.some((file) => file.endsWith('.py'))\n}\n\nexport const build = async (listener: BuildListener): Promise<Builder> => {\n const builder = new Builder(projectDir, listener)\n const stepFiles = getStepFiles(projectDir)\n const streamFiles = getStreamFiles(projectDir)\n\n if (stepFiles.length === 0) {\n throw new Error('Project contains no steps, please add some steps before building')\n }\n\n // Register language-specific builders\n builder.registerBuilder('node', new NodeBuilder(builder, listener))\n\n fs.rmSync(distDir, { recursive: true, force: true })\n fs.mkdirSync(distDir, { recursive: true })\n\n const lockedData = new LockedData(projectDir, new MemoryStreamAdapterManager(), new NoPrinter())\n\n const hasPython = hasPythonSteps([...stepFiles, ...streamFiles])\n const pythonValidation = await validatePythonEnvironment({ baseDir: projectDir, hasPythonFiles: hasPython })\n if (!pythonValidation.success) {\n throw new BuildError(\n BuildErrorType.COMPILATION,\n undefined,\n 'Python environment validation failed. Please run the install command to set up your Python environment.',\n )\n }\n\n if (hasPython) {\n builder.registerBuilder('python', new PythonBuilder(builder, listener))\n }\n\n const invalidSteps = await collectFlows(projectDir, lockedData).catch((err) => {\n const errorMessage = err.filePath ? `Build error in ${err.filePath}` : 'Build error'\n\n const finalMessage = `${errorMessage}\\nPlease check the logs above for details`\n\n throw new BuildError(BuildErrorType.COMPILATION, err.filePath, finalMessage, err)\n })\n\n if (invalidSteps.length > 0) {\n throw new Error('Project contains invalid steps, please fix them before building')\n }\n\n await Promise.all(lockedData.activeSteps.map((step) => builder.buildStep(step)))\n await builder.buildApiSteps(lockedData.activeSteps.filter(isApiStep))\n\n const streams = lockedData.listStreams()\n\n for (const stream of streams) {\n if (stream.config.baseConfig.storageType === 'default') {\n builder.registerStateStream(stream)\n } else {\n listener.onWarning(stream.filePath, 'Custom streams are not supported yet in the cloud')\n }\n }\n\n const stepsFile: StepsConfigFile = {\n steps: builder.stepsConfig,\n streams: builder.streamsConfig,\n routers: builder.routersConfig,\n }\n fs.writeFileSync(stepsConfigPath, JSON.stringify(stepsFile, null, 2))\n\n return builder\n}\n"],"mappings":";;;;;;;;;;;AAWA,MAAM,kBAAkB,cAAwB;AAC9C,QAAO,UAAU,MAAM,SAAS,KAAK,SAAS,MAAM,CAAC;;AAGvD,MAAa,QAAQ,OAAO,aAA8C;CACxE,MAAM,UAAU,IAAI,QAAQ,YAAY,SAAS;CACjD,MAAM,YAAY,aAAa,WAAW;CAC1C,MAAM,cAAc,eAAe,WAAW;AAE9C,KAAI,UAAU,WAAW,EACvB,OAAM,IAAI,MAAM,mEAAmE;AAIrF,SAAQ,gBAAgB,QAAQ,IAAI,YAAY,SAAS,SAAS,CAAC;AAEnE,IAAG,OAAO,SAAS;EAAE,WAAW;EAAM,OAAO;EAAM,CAAC;AACpD,IAAG,UAAU,SAAS,EAAE,WAAW,MAAM,CAAC;CAE1C,MAAM,aAAa,IAAI,WAAW,YAAY,IAAI,4BAA4B,EAAE,IAAI,WAAW,CAAC;CAEhG,MAAM,YAAY,eAAe,CAAC,GAAG,WAAW,GAAG,YAAY,CAAC;AAEhE,KAAI,EADqB,MAAM,0BAA0B;EAAE,SAAS;EAAY,gBAAgB;EAAW,CAAC,EACtF,QACpB,OAAM,IAAI,WACR,eAAe,aACf,QACA,0GACD;AAGH,KAAI,UACF,SAAQ,gBAAgB,UAAU,IAAI,cAAc,SAAS,SAAS,CAAC;AAWzE,MARqB,MAAM,aAAa,YAAY,WAAW,CAAC,OAAO,QAAQ;EAG7E,MAAM,eAAe,GAFA,IAAI,WAAW,kBAAkB,IAAI,aAAa,cAElC;AAErC,QAAM,IAAI,WAAW,eAAe,aAAa,IAAI,UAAU,cAAc,IAAI;GACjF,EAEe,SAAS,EACxB,OAAM,IAAI,MAAM,kEAAkE;AAGpF,OAAM,QAAQ,IAAI,WAAW,YAAY,KAAK,SAAS,QAAQ,UAAU,KAAK,CAAC,CAAC;AAChF,OAAM,QAAQ,cAAc,WAAW,YAAY,OAAO,UAAU,CAAC;CAErE,MAAM,UAAU,WAAW,aAAa;AAExC,MAAK,MAAM,UAAU,QACnB,KAAI,OAAO,OAAO,WAAW,gBAAgB,UAC3C,SAAQ,oBAAoB,OAAO;KAEnC,UAAS,UAAU,OAAO,UAAU,oDAAoD;CAI5F,MAAMA,YAA6B;EACjC,OAAO,QAAQ;EACf,SAAS,QAAQ;EACjB,SAAS,QAAQ;EAClB;AACD,IAAG,cAAc,iBAAiB,KAAK,UAAU,WAAW,MAAM,EAAE,CAAC;AAErE,QAAO"}
1
+ {"version":3,"file":"build.mjs","names":["stepsFile: StepsConfigFile"],"sources":["../../../src/cloud/new-deployment/build.ts"],"sourcesContent":["import { isApiStep, LockedData, MemoryStreamAdapterManager, NoPrinter } from '@motiadev/core'\nimport fs from 'fs'\nimport { collectFlows, getStepFiles, getStreamFiles } from '../../generate-locked-data'\nimport { BuildError, BuildErrorType } from '../../utils/errors/build.error'\nimport { Builder, type StepsConfigFile } from '../build/builder'\nimport { NodeBuilder } from '../build/builders/node/index'\nimport { PythonBuilder } from '../build/builders/python/index'\nimport { distDir, projectDir, stepsConfigPath } from './constants'\nimport type { BuildListener } from './listeners/listener.types'\n\nconst hasPythonSteps = (stepFiles: string[]) => {\n return stepFiles.some((file) => file.endsWith('.py'))\n}\n\nexport const build = async (listener: BuildListener): Promise<Builder> => {\n const builder = new Builder(projectDir, listener)\n const stepFiles = getStepFiles(projectDir)\n const streamFiles = getStreamFiles(projectDir)\n\n if (stepFiles.length === 0) {\n throw new Error('Project contains no steps, please add some steps before building')\n }\n\n // Register language-specific builders\n builder.registerBuilder('node', new NodeBuilder(builder, listener))\n\n fs.rmSync(distDir, { recursive: true, force: true })\n fs.mkdirSync(distDir, { recursive: true })\n\n const lockedData = new LockedData(projectDir, new MemoryStreamAdapterManager(), new NoPrinter())\n\n if (hasPythonSteps([...stepFiles, ...streamFiles])) {\n builder.registerBuilder('python', new PythonBuilder(builder, listener))\n }\n\n const invalidSteps = await collectFlows(projectDir, lockedData).catch((err) => {\n const errorMessage = err.filePath ? `Build error in ${err.filePath}` : 'Build error'\n\n const finalMessage = `${errorMessage}\\nPlease check the logs above for details`\n\n throw new BuildError(BuildErrorType.COMPILATION, err.filePath, finalMessage, err)\n })\n\n if (invalidSteps.length > 0) {\n throw new Error('Project contains invalid steps, please fix them before building')\n }\n\n await Promise.all(lockedData.activeSteps.map((step) => builder.buildStep(step)))\n await builder.buildApiSteps(lockedData.activeSteps.filter(isApiStep))\n\n const streams = lockedData.listStreams()\n\n for (const stream of streams) {\n if (stream.config.baseConfig.storageType === 'default') {\n builder.registerStateStream(stream)\n } else {\n listener.onWarning(stream.filePath, 'Custom streams are not supported yet in the cloud')\n }\n }\n\n const stepsFile: StepsConfigFile = {\n steps: builder.stepsConfig,\n streams: builder.streamsConfig,\n routers: builder.routersConfig,\n }\n fs.writeFileSync(stepsConfigPath, JSON.stringify(stepsFile, null, 2))\n\n return builder\n}\n"],"mappings":";;;;;;;;;;AAUA,MAAM,kBAAkB,cAAwB;AAC9C,QAAO,UAAU,MAAM,SAAS,KAAK,SAAS,MAAM,CAAC;;AAGvD,MAAa,QAAQ,OAAO,aAA8C;CACxE,MAAM,UAAU,IAAI,QAAQ,YAAY,SAAS;CACjD,MAAM,YAAY,aAAa,WAAW;CAC1C,MAAM,cAAc,eAAe,WAAW;AAE9C,KAAI,UAAU,WAAW,EACvB,OAAM,IAAI,MAAM,mEAAmE;AAIrF,SAAQ,gBAAgB,QAAQ,IAAI,YAAY,SAAS,SAAS,CAAC;AAEnE,IAAG,OAAO,SAAS;EAAE,WAAW;EAAM,OAAO;EAAM,CAAC;AACpD,IAAG,UAAU,SAAS,EAAE,WAAW,MAAM,CAAC;CAE1C,MAAM,aAAa,IAAI,WAAW,YAAY,IAAI,4BAA4B,EAAE,IAAI,WAAW,CAAC;AAEhG,KAAI,eAAe,CAAC,GAAG,WAAW,GAAG,YAAY,CAAC,CAChD,SAAQ,gBAAgB,UAAU,IAAI,cAAc,SAAS,SAAS,CAAC;AAWzE,MARqB,MAAM,aAAa,YAAY,WAAW,CAAC,OAAO,QAAQ;EAG7E,MAAM,eAAe,GAFA,IAAI,WAAW,kBAAkB,IAAI,aAAa,cAElC;AAErC,QAAM,IAAI,WAAW,eAAe,aAAa,IAAI,UAAU,cAAc,IAAI;GACjF,EAEe,SAAS,EACxB,OAAM,IAAI,MAAM,kEAAkE;AAGpF,OAAM,QAAQ,IAAI,WAAW,YAAY,KAAK,SAAS,QAAQ,UAAU,KAAK,CAAC,CAAC;AAChF,OAAM,QAAQ,cAAc,WAAW,YAAY,OAAO,UAAU,CAAC;CAErE,MAAM,UAAU,WAAW,aAAa;AAExC,MAAK,MAAM,UAAU,QACnB,KAAI,OAAO,OAAO,WAAW,gBAAgB,UAC3C,SAAQ,oBAAoB,OAAO;KAEnC,UAAS,UAAU,OAAO,UAAU,oDAAoD;CAI5F,MAAMA,YAA6B;EACjC,OAAO,QAAQ;EACf,SAAS,QAAQ;EACjB,SAAS,QAAQ;EAClB;AACD,IAAG,cAAc,iBAAiB,KAAK,UAAU,WAAW,MAAM,EAAE,CAAC;AAErE,QAAO"}
@@ -1,10 +1,10 @@
1
1
  import { executeCommand } from "../utils/execute-command.mjs";
2
- import { checkIfDirectoryExists, checkIfFileExists } from "./utils.mjs";
3
- import { getPackageManager } from "../utils/get-package-manager.mjs";
4
2
  import { version } from "../version.mjs";
5
3
  import { generateTypes } from "../generate-types.mjs";
6
4
  import { pythonInstall } from "../install.mjs";
7
5
  import { pluginDependencies } from "../plugins/plugin-dependencies.mjs";
6
+ import { checkIfDirectoryExists, checkIfFileExists } from "./utils.mjs";
7
+ import { getPackageManager } from "../utils/get-package-manager.mjs";
8
8
  import { pullRules } from "./pull-rules.mjs";
9
9
  import { setupTemplate } from "./setup-template.mjs";
10
10
  import fs from "fs";
package/dist/dev.mjs CHANGED
@@ -1,5 +1,4 @@
1
1
  import { activatePythonVenv } from "./utils/activate-python-env.mjs";
2
- import { validatePythonEnvironment } from "./utils/validate-python-environment.mjs";
3
2
  import { generateLockedData, getStepFiles, getStreamFiles } from "./generate-locked-data.mjs";
4
3
  import { version } from "./version.mjs";
5
4
  import { identifyUser } from "./utils/analytics.mjs";
@@ -32,12 +31,7 @@ const dev = async (port, hostname, disableVerbose, enableMermaid, motiaFileStora
32
31
  total_step_files: stepFiles.length,
33
32
  project_name: getProjectIdentifier(baseDir)
34
33
  });
35
- const pythonValidation = await validatePythonEnvironment({
36
- baseDir,
37
- hasPythonFiles
38
- });
39
- if (!pythonValidation.success) process.exit(1);
40
- if (pythonValidation.hasPythonFiles) {
34
+ if (hasPythonFiles) {
41
35
  activatePythonVenv({
42
36
  baseDir,
43
37
  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 { 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
+ {"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 +1 @@
1
- {"version":3,"file":"generate-locked-data.d.mts","names":[],"sources":["../src/generate-locked-data.ts"],"sourcesContent":[],"mappings":";;;;AA0Da,cAAA,YA0FZ,EAAA,CAAA,UAAA,EAAA,MAAA,EAAA,UAAA,EA1FkE,UA0FlE,EAAA,GA1F+E,OA0F/E,CA1FuF,IA0FvF,EAAA,CAAA;KAEI,iBAAA,GA5F8D;EAAqB,YAAA,EA6FxE,gBA7FwE,CAAA,cAAA,CAAA;EAAR,aAAA,CAAA,EA8F9D,UA9F8D;CAAO;AA4FlF,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":";;;;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,7 +1,6 @@
1
1
  import { activatePythonVenv } from "./utils/activate-python-env.mjs";
2
2
  import { CompilationError } from "./utils/errors/compilation.error.mjs";
3
3
  import { LockedDataGenerationError } from "./utils/errors/locked-data-generation.error.mjs";
4
- import { validatePythonEnvironment } from "./utils/validate-python-environment.mjs";
5
4
  import { LockedData, MemoryStreamAdapterManager, NoPrinter, Printer, getStepConfig, getStreamConfig } from "@motiadev/core";
6
5
  import { randomUUID } from "crypto";
7
6
  import { existsSync } from "fs";
@@ -54,12 +53,7 @@ const collectFlows = async (projectDir, lockedData) => {
54
53
  absolute: true,
55
54
  cwd: srcDir
56
55
  }) : []];
57
- const hasPythonFiles = stepFiles.some((file) => file.endsWith(".py")) || streamFiles.some((file) => file.endsWith(".py"));
58
- if (!(await validatePythonEnvironment({
59
- baseDir: projectDir,
60
- hasPythonFiles
61
- })).success) throw new LockedDataGenerationError("Python environment validation failed. Please run the install command to set up your Python environment.");
62
- if (hasPythonFiles) activatePythonVenv({ baseDir: projectDir });
56
+ if (stepFiles.some((file) => file.endsWith(".py")) || streamFiles.some((file) => file.endsWith(".py"))) activatePythonVenv({ baseDir: projectDir });
63
57
  for (const filePath of stepFiles) try {
64
58
  const config = await getStepConfig(filePath, projectDir);
65
59
  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'\nimport { validatePythonEnvironment } from './utils/validate-python-environment'\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 const pythonValidation = await validatePythonEnvironment({ baseDir: projectDir, hasPythonFiles })\n if (!pythonValidation.success) {\n throw new LockedDataGenerationError(\n 'Python environment validation failed. Please run the install command to set up your Python environment.',\n )\n }\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 { 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,7 +1,7 @@
1
1
  import { executeCommand } from "../utils/execute-command.mjs";
2
- import { getPackageManager } from "../utils/get-package-manager.mjs";
3
2
  import { version } from "../version.mjs";
4
3
  import { pluginDependencies } from "./plugin-dependencies.mjs";
4
+ import { getPackageManager } from "../utils/get-package-manager.mjs";
5
5
  import fs from "node:fs";
6
6
  import path from "node:path";
7
7
 
package/dist/start.mjs CHANGED
@@ -1,5 +1,4 @@
1
1
  import { activatePythonVenv } from "./utils/activate-python-env.mjs";
2
- import { validatePythonEnvironment } from "./utils/validate-python-environment.mjs";
3
2
  import { generateLockedData, getStepFiles, getStreamFiles } from "./generate-locked-data.mjs";
4
3
  import { version } from "./version.mjs";
5
4
  import { loadMotiaConfig } from "./load-motia-config.mjs";
@@ -18,12 +17,7 @@ import { RedisStreamAdapterManager } from "@motiadev/adapter-redis-streams";
18
17
  const start = async (port, hostname, disableVerbose, motiaFileStorageDir) => {
19
18
  const baseDir = process.cwd();
20
19
  const isVerbose = !disableVerbose;
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) {
20
+ if ([...getStepFiles(baseDir), ...getStreamFiles(baseDir)].some((file) => file.endsWith(".py"))) {
27
21
  console.log("⚙️ Activating Python environment...");
28
22
  activatePythonVenv({
29
23
  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 { 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
+ {"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,5 +1,6 @@
1
1
  import { internalLogger } from "./internal-logger.mjs";
2
2
  import { findPythonSitePackagesDir } from "./python-version-utils.mjs";
3
+ import fs from "fs";
3
4
  import path from "path";
4
5
 
5
6
  //#region src/utils/activate-python-env.ts
@@ -16,15 +17,20 @@ const activatePythonVenv = ({ baseDir, isVerbose = false, pythonVersion = "3.13"
16
17
  baseDir,
17
18
  pythonVersion
18
19
  });
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));
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");
28
34
  }
29
35
  };
30
36
 
@@ -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 // 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
+ {"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,17 +1,11 @@
1
1
  import { checkIfFileExists } from "../create/utils.mjs";
2
- import path from "path";
3
2
 
4
3
  //#region src/utils/get-package-manager.ts
5
4
  const getPackageManager = (dir) => {
6
- let currentDir = dir;
7
- while (currentDir !== path.dirname(currentDir)) {
8
- if (checkIfFileExists(currentDir, "yarn.lock")) return "yarn";
9
- else if (checkIfFileExists(currentDir, "pnpm-lock.yaml")) return "pnpm";
10
- else if (checkIfFileExists(currentDir, "package-lock.json")) return "npm";
11
- else if (checkIfFileExists(currentDir, "bun.lockb") || checkIfFileExists(currentDir, "bun.lock")) return "bun";
12
- currentDir = path.dirname(currentDir);
13
- }
14
- return "npm";
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";
15
9
  };
16
10
 
17
11
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"get-package-manager.mjs","names":[],"sources":["../../src/utils/get-package-manager.ts"],"sourcesContent":["import path from 'path'\nimport { checkIfFileExists } from '../create/utils'\n\nexport const getPackageManager = (dir: string): string => {\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 currentDir = path.dirname(currentDir)\n }\n\n return 'npm'\n}\n"],"mappings":";;;;AAGA,MAAa,qBAAqB,QAAwB;CACxD,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;AAET,eAAa,KAAK,QAAQ,WAAW;;AAGvC,QAAO"}
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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "motia",
3
3
  "description": "Build production-grade backends with a single primitive. APIs, background jobs, Queues, Workflows, and AI agents - unified in one system with built-in State management, Streaming, and Observability.",
4
- "version": "0.15.4-beta.170-590272",
4
+ "version": "0.15.5-beta.171",
5
5
  "license": "Elastic-2.0",
6
6
  "type": "module",
7
7
  "repository": {
@@ -46,13 +46,13 @@
46
46
  "table": "^6.9.0",
47
47
  "ts-node": "^10.9.2",
48
48
  "zod": "^4.1.12",
49
- "@motiadev/adapter-redis-cron": "0.15.4-beta.170-590272",
50
- "@motiadev/adapter-bullmq-events": "0.15.4-beta.170-590272",
51
- "@motiadev/adapter-redis-state": "0.15.4-beta.170-590272",
52
- "@motiadev/adapter-redis-streams": "0.15.4-beta.170-590272",
53
- "@motiadev/workbench": "0.15.4-beta.170-590272",
54
- "@motiadev/stream-client-node": "0.15.4-beta.170-590272",
55
- "@motiadev/core": "0.15.4-beta.170-590272"
49
+ "@motiadev/adapter-bullmq-events": "0.15.5-beta.171",
50
+ "@motiadev/adapter-redis-cron": "0.15.5-beta.171",
51
+ "@motiadev/adapter-redis-state": "0.15.5-beta.171",
52
+ "@motiadev/core": "0.15.5-beta.171",
53
+ "@motiadev/stream-client-node": "0.15.5-beta.171",
54
+ "@motiadev/workbench": "0.15.5-beta.171",
55
+ "@motiadev/adapter-redis-streams": "0.15.5-beta.171"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@amplitude/analytics-types": "^2.9.2",
@@ -1,84 +0,0 @@
1
- import { internalLogger } from "./internal-logger.mjs";
2
- import { getPythonCommand } from "./python-version-utils.mjs";
3
- import { getPackageManager } from "./get-package-manager.mjs";
4
- import fs from "fs";
5
- import path from "path";
6
-
7
- //#region src/utils/validate-python-environment.ts
8
- function getInstallCommand(baseDir) {
9
- switch (getPackageManager(baseDir)) {
10
- case "yarn": return "yarn install";
11
- case "pnpm": return "pnpm install";
12
- case "bun": return "bun install";
13
- case "npm":
14
- default: return "npm install";
15
- }
16
- }
17
- async function validatePythonEnvironment({ baseDir, hasPythonFiles, pythonVersion = "3.13" }) {
18
- if (!hasPythonFiles) return {
19
- success: true,
20
- hasPythonFiles: false
21
- };
22
- try {
23
- await getPythonCommand(pythonVersion, baseDir);
24
- } catch {
25
- internalLogger.error("Python is not installed");
26
- internalLogger.info("Python files were detected in your project but Python 3 is not available");
27
- internalLogger.info("Please install Python 3.10 or higher: https://www.python.org/downloads/");
28
- return {
29
- success: false,
30
- hasPythonFiles: true
31
- };
32
- }
33
- const venvPath = path.join(baseDir, "python_modules");
34
- if (!fs.existsSync(venvPath)) {
35
- const installCmd = getInstallCommand(baseDir);
36
- internalLogger.error("Python environment not configured");
37
- internalLogger.info("The python_modules directory was not found");
38
- internalLogger.info(`Run '${installCmd}' to set up your Python environment`);
39
- return {
40
- success: false,
41
- hasPythonFiles: true
42
- };
43
- }
44
- const libPath = path.join(venvPath, "lib");
45
- if (!fs.existsSync(libPath)) {
46
- const installCmd = getInstallCommand(baseDir);
47
- internalLogger.error("Python environment is incomplete");
48
- internalLogger.info("The python_modules directory exists but appears to be corrupted");
49
- internalLogger.info(`Run '${installCmd}' to recreate your Python environment`);
50
- return {
51
- success: false,
52
- hasPythonFiles: true
53
- };
54
- }
55
- try {
56
- if (fs.readdirSync(libPath).filter((item) => item.startsWith("python3")).length === 0) {
57
- const installCmd = getInstallCommand(baseDir);
58
- internalLogger.error("Python environment is incomplete");
59
- internalLogger.info("The python_modules/lib directory exists but contains no Python version directories");
60
- internalLogger.info(`Run '${installCmd}' to recreate your Python environment`);
61
- return {
62
- success: false,
63
- hasPythonFiles: true
64
- };
65
- }
66
- } catch (error) {
67
- const installCmd = getInstallCommand(baseDir);
68
- internalLogger.error("Python environment is incomplete");
69
- internalLogger.info("The python_modules/lib directory cannot be read");
70
- internalLogger.info(`Run '${installCmd}' to recreate your Python environment`);
71
- return {
72
- success: false,
73
- hasPythonFiles: true
74
- };
75
- }
76
- return {
77
- success: true,
78
- hasPythonFiles: true
79
- };
80
- }
81
-
82
- //#endregion
83
- export { validatePythonEnvironment };
84
- //# sourceMappingURL=validate-python-environment.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"validate-python-environment.mjs","names":["error: any"],"sources":["../../src/utils/validate-python-environment.ts"],"sourcesContent":["import fs from 'fs'\nimport path from 'path'\nimport { getPackageManager } from './get-package-manager'\nimport { internalLogger } from './internal-logger'\nimport { getPythonCommand } from './python-version-utils'\n\nexport interface ValidationResult {\n success: boolean\n hasPythonFiles: boolean\n}\n\ninterface ValidateConfig {\n baseDir: string\n hasPythonFiles: boolean\n pythonVersion?: string\n}\n\nfunction getInstallCommand(baseDir: string): string {\n const pm = getPackageManager(baseDir)\n switch (pm) {\n case 'yarn':\n return 'yarn install'\n case 'pnpm':\n return 'pnpm install'\n case 'bun':\n return 'bun install'\n case 'npm':\n default:\n return 'npm install'\n }\n}\n\nexport async function validatePythonEnvironment({\n baseDir,\n hasPythonFiles,\n pythonVersion = '3.13',\n}: ValidateConfig): Promise<ValidationResult> {\n if (!hasPythonFiles) {\n return { success: true, hasPythonFiles: false }\n }\n\n try {\n await getPythonCommand(pythonVersion, baseDir)\n } catch {\n internalLogger.error('Python is not installed')\n internalLogger.info('Python files were detected in your project but Python 3 is not available')\n internalLogger.info('Please install Python 3.10 or higher: https://www.python.org/downloads/')\n return { success: false, hasPythonFiles: true }\n }\n\n const venvPath = path.join(baseDir, 'python_modules')\n if (!fs.existsSync(venvPath)) {\n const installCmd = getInstallCommand(baseDir)\n internalLogger.error('Python environment not configured')\n internalLogger.info('The python_modules directory was not found')\n internalLogger.info(`Run '${installCmd}' to set up your Python environment`)\n return { success: false, hasPythonFiles: true }\n }\n\n const libPath = path.join(venvPath, 'lib')\n if (!fs.existsSync(libPath)) {\n const installCmd = getInstallCommand(baseDir)\n internalLogger.error('Python environment is incomplete')\n internalLogger.info('The python_modules directory exists but appears to be corrupted')\n internalLogger.info(`Run '${installCmd}' to recreate your Python environment`)\n return { success: false, hasPythonFiles: true }\n }\n\n try {\n const libContents = fs.readdirSync(libPath)\n const pythonDirs = libContents.filter((item) => item.startsWith('python3'))\n\n if (pythonDirs.length === 0) {\n const installCmd = getInstallCommand(baseDir)\n internalLogger.error('Python environment is incomplete')\n internalLogger.info('The python_modules/lib directory exists but contains no Python version directories')\n internalLogger.info(`Run '${installCmd}' to recreate your Python environment`)\n return { success: false, hasPythonFiles: true }\n }\n } catch (error: any) {\n const installCmd = getInstallCommand(baseDir)\n internalLogger.error('Python environment is incomplete')\n internalLogger.info('The python_modules/lib directory cannot be read')\n internalLogger.info(`Run '${installCmd}' to recreate your Python environment`)\n return { success: false, hasPythonFiles: true }\n }\n\n return { success: true, hasPythonFiles: true }\n}\n"],"mappings":";;;;;;;AAiBA,SAAS,kBAAkB,SAAyB;AAElD,SADW,kBAAkB,QAAQ,EACrC;EACE,KAAK,OACH,QAAO;EACT,KAAK,OACH,QAAO;EACT,KAAK,MACH,QAAO;EACT,KAAK;EACL,QACE,QAAO;;;AAIb,eAAsB,0BAA0B,EAC9C,SACA,gBACA,gBAAgB,UAC4B;AAC5C,KAAI,CAAC,eACH,QAAO;EAAE,SAAS;EAAM,gBAAgB;EAAO;AAGjD,KAAI;AACF,QAAM,iBAAiB,eAAe,QAAQ;SACxC;AACN,iBAAe,MAAM,0BAA0B;AAC/C,iBAAe,KAAK,2EAA2E;AAC/F,iBAAe,KAAK,0EAA0E;AAC9F,SAAO;GAAE,SAAS;GAAO,gBAAgB;GAAM;;CAGjD,MAAM,WAAW,KAAK,KAAK,SAAS,iBAAiB;AACrD,KAAI,CAAC,GAAG,WAAW,SAAS,EAAE;EAC5B,MAAM,aAAa,kBAAkB,QAAQ;AAC7C,iBAAe,MAAM,oCAAoC;AACzD,iBAAe,KAAK,6CAA6C;AACjE,iBAAe,KAAK,QAAQ,WAAW,qCAAqC;AAC5E,SAAO;GAAE,SAAS;GAAO,gBAAgB;GAAM;;CAGjD,MAAM,UAAU,KAAK,KAAK,UAAU,MAAM;AAC1C,KAAI,CAAC,GAAG,WAAW,QAAQ,EAAE;EAC3B,MAAM,aAAa,kBAAkB,QAAQ;AAC7C,iBAAe,MAAM,mCAAmC;AACxD,iBAAe,KAAK,kEAAkE;AACtF,iBAAe,KAAK,QAAQ,WAAW,uCAAuC;AAC9E,SAAO;GAAE,SAAS;GAAO,gBAAgB;GAAM;;AAGjD,KAAI;AAIF,MAHoB,GAAG,YAAY,QAAQ,CACZ,QAAQ,SAAS,KAAK,WAAW,UAAU,CAAC,CAE5D,WAAW,GAAG;GAC3B,MAAM,aAAa,kBAAkB,QAAQ;AAC7C,kBAAe,MAAM,mCAAmC;AACxD,kBAAe,KAAK,qFAAqF;AACzG,kBAAe,KAAK,QAAQ,WAAW,uCAAuC;AAC9E,UAAO;IAAE,SAAS;IAAO,gBAAgB;IAAM;;UAE1CA,OAAY;EACnB,MAAM,aAAa,kBAAkB,QAAQ;AAC7C,iBAAe,MAAM,mCAAmC;AACxD,iBAAe,KAAK,kDAAkD;AACtE,iBAAe,KAAK,QAAQ,WAAW,uCAAuC;AAC9E,SAAO;GAAE,SAAS;GAAO,gBAAgB;GAAM;;AAGjD,QAAO;EAAE,SAAS;EAAM,gBAAgB;EAAM"}