motia 0.8.6-beta.143 → 0.8.6-beta.144-824340

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 (60) hide show
  1. package/dist/cjs/cli.js +2 -0
  2. package/dist/cjs/constants.js +6 -1
  3. package/dist/cjs/create/interactive.d.ts +1 -0
  4. package/dist/cjs/create/interactive.js +21 -5
  5. package/dist/cjs/create/templates/hello/motia-workbench.json +15 -0
  6. package/dist/cjs/create/templates/hello/motia.config.ts.txt +9 -0
  7. package/dist/cjs/create/templates/hello/steps/hello/hello-api.step.ts.txt +46 -0
  8. package/dist/cjs/create/templates/hello/steps/hello/process-greeting.step.ts.txt +43 -0
  9. package/dist/cjs/create/templates/hello_js/motia-workbench.json +13 -0
  10. package/dist/cjs/create/templates/hello_js/motia.config.ts.txt +9 -0
  11. package/dist/cjs/create/templates/hello_js/steps/hello/hello-api.step.js.txt +47 -0
  12. package/dist/cjs/create/templates/hello_js/steps/hello/process-greeting.step.js.txt +44 -0
  13. package/dist/cjs/create/templates/hello_python/motia-workbench.json +13 -0
  14. package/dist/cjs/create/templates/hello_python/motia.config.ts.txt +9 -0
  15. package/dist/cjs/create/templates/hello_python/requirements.txt +2 -0
  16. package/dist/cjs/create/templates/hello_python/steps/hello/hello_api_step.py.txt +74 -0
  17. package/dist/cjs/create/templates/hello_python/steps/hello/process_greeting_step.py.txt +68 -0
  18. package/dist/cjs/create/templates/index.js +3 -0
  19. package/dist/cjs/create/templates/index.ts +3 -0
  20. package/dist/cjs/create/templates/nodejs/motia.config.ts.txt +1 -1
  21. package/dist/cjs/create/templates/python/motia.config.ts.txt +1 -1
  22. package/dist/esm/cli.js +2 -0
  23. package/dist/esm/constants.js +3 -1
  24. package/dist/esm/create/interactive.d.ts +1 -0
  25. package/dist/esm/create/interactive.js +21 -5
  26. package/dist/esm/create/templates/hello/motia-workbench.json +15 -0
  27. package/dist/esm/create/templates/hello/motia.config.ts.txt +9 -0
  28. package/dist/esm/create/templates/hello/steps/hello/hello-api.step.ts.txt +46 -0
  29. package/dist/esm/create/templates/hello/steps/hello/process-greeting.step.ts.txt +43 -0
  30. package/dist/esm/create/templates/hello_js/motia-workbench.json +13 -0
  31. package/dist/esm/create/templates/hello_js/motia.config.ts.txt +9 -0
  32. package/dist/esm/create/templates/hello_js/steps/hello/hello-api.step.js.txt +47 -0
  33. package/dist/esm/create/templates/hello_js/steps/hello/process-greeting.step.js.txt +44 -0
  34. package/dist/esm/create/templates/hello_python/motia-workbench.json +13 -0
  35. package/dist/esm/create/templates/hello_python/motia.config.ts.txt +9 -0
  36. package/dist/esm/create/templates/hello_python/requirements.txt +2 -0
  37. package/dist/esm/create/templates/hello_python/steps/hello/hello_api_step.py.txt +74 -0
  38. package/dist/esm/create/templates/hello_python/steps/hello/process_greeting_step.py.txt +68 -0
  39. package/dist/esm/create/templates/index.js +3 -0
  40. package/dist/esm/create/templates/index.ts +3 -0
  41. package/dist/esm/create/templates/nodejs/motia.config.ts.txt +1 -1
  42. package/dist/esm/create/templates/python/motia.config.ts.txt +1 -1
  43. package/dist/types/create/interactive.d.ts +1 -0
  44. package/package.json +4 -4
  45. /package/dist/cjs/create/templates/nodejs/{steps → tutorial}/petstore/api.step.ts-features.json.txt +0 -0
  46. /package/dist/cjs/create/templates/nodejs/{steps → tutorial}/petstore/process-food-order.step.ts-features.json.txt +0 -0
  47. /package/dist/cjs/create/templates/nodejs/{steps → tutorial}/petstore/state-audit-cron.step.ts-features.json.txt +0 -0
  48. /package/dist/cjs/create/templates/nodejs/{tutorial.tsx.txt → tutorial/tutorial.tsx.txt} +0 -0
  49. /package/dist/cjs/create/templates/python/{steps → tutorial}/petstore/api_step.py-features.json.txt +0 -0
  50. /package/dist/cjs/create/templates/python/{steps → tutorial}/petstore/process_food_order_step.py-features.json.txt +0 -0
  51. /package/dist/cjs/create/templates/python/{steps → tutorial}/petstore/state_audit_cron_step.py-features.json.txt +0 -0
  52. /package/dist/cjs/create/templates/python/{tutorial.tsx.txt → tutorial/tutorial.tsx.txt} +0 -0
  53. /package/dist/esm/create/templates/nodejs/{steps → tutorial}/petstore/api.step.ts-features.json.txt +0 -0
  54. /package/dist/esm/create/templates/nodejs/{steps → tutorial}/petstore/process-food-order.step.ts-features.json.txt +0 -0
  55. /package/dist/esm/create/templates/nodejs/{steps → tutorial}/petstore/state-audit-cron.step.ts-features.json.txt +0 -0
  56. /package/dist/esm/create/templates/nodejs/{tutorial.tsx.txt → tutorial/tutorial.tsx.txt} +0 -0
  57. /package/dist/esm/create/templates/python/{steps → tutorial}/petstore/api_step.py-features.json.txt +0 -0
  58. /package/dist/esm/create/templates/python/{steps → tutorial}/petstore/process_food_order_step.py-features.json.txt +0 -0
  59. /package/dist/esm/create/templates/python/{steps → tutorial}/petstore/state_audit_cron_step.py-features.json.txt +0 -0
  60. /package/dist/esm/create/templates/python/{tutorial.tsx.txt → tutorial/tutorial.tsx.txt} +0 -0
package/dist/cjs/cli.js CHANGED
@@ -24,6 +24,7 @@ commander_1.program
24
24
  .command('create [name]')
25
25
  .description('Create a new motia project')
26
26
  .option('-t, --template <template>', 'The template to use for your project')
27
+ .option('-p, --plugin', 'Create a plugin project')
27
28
  .option('-i, --interactive', 'Use interactive prompts to create project') // it's default
28
29
  .option('-c, --confirm', 'Confirm the project creation', false)
29
30
  .action((projectName, options) => {
@@ -33,6 +34,7 @@ commander_1.program
33
34
  await createInteractive({
34
35
  name: arg.name,
35
36
  template: arg.template,
37
+ plugin: !!arg.plugin,
36
38
  confirm: !!arg.confirm,
37
39
  }, context);
38
40
  })(mergedArgs);
@@ -1,6 +1,11 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.isTutorialDisabled = exports.workbenchBase = void 0;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
4
9
  function getWorkbenchBase() {
5
10
  const basePath = process.env.MOTIA_WORKBENCH_BASE;
6
11
  if (basePath === '/') {
@@ -9,4 +14,4 @@ function getWorkbenchBase() {
9
14
  return basePath && basePath[0] !== '/' ? `/${basePath}` : basePath || '';
10
15
  }
11
16
  exports.workbenchBase = getWorkbenchBase();
12
- exports.isTutorialDisabled = process.env.MOTIA_TUTORIAL_DISABLED === 'true';
17
+ exports.isTutorialDisabled = process.env.MOTIA_TUTORIAL_DISABLED === 'true' || !node_fs_1.default.existsSync(node_path_1.default.join(process.cwd(), 'tutorial/tutorial.tsx'));
@@ -2,6 +2,7 @@ import type { CliContext } from '../cloud/config-utils';
2
2
  interface CreateInteractiveArgs {
3
3
  name?: string;
4
4
  template?: string;
5
+ plugin?: boolean;
5
6
  confirm?: boolean;
6
7
  }
7
8
  export declare const createInteractive: (args: CreateInteractiveArgs, context: CliContext) => Promise<void>;
@@ -8,16 +8,32 @@ const colors_1 = __importDefault(require("colors"));
8
8
  const inquirer_1 = __importDefault(require("inquirer"));
9
9
  const index_1 = require("./index");
10
10
  const choices = {
11
- nodejs: 'Base (TypeScript)',
12
- python: 'Base (Python)',
13
- plugin: 'Plugin (TypeScript)',
11
+ nodejs: 'Tutorial (TypeScript)',
12
+ python: 'Tutorial (Python)',
13
+ 'starter-typescript': 'Starter (TypeScript)',
14
+ 'starter-javascript': 'Starter (JavaScript)',
15
+ 'starter-python': 'Starter (Python)',
14
16
  };
15
17
  const createInteractive = async (args, context) => {
16
- context.log('welcome', (message) => message.append('\n🚀 ' + colors_1.default.bold('Welcome to Motia Project Creator!')));
18
+ context.log('welcome', (message) => message.append(`\n🚀 ${colors_1.default.bold(args.plugin ? 'Welcome to Motia Plugin Creator!' : 'Welcome to Motia Project Creator!')}`));
17
19
  const questions = [];
18
20
  let name = args.name;
19
21
  let template = args.template;
20
- if (!args.template) {
22
+ if (args.plugin) {
23
+ if (!args.name) {
24
+ context.log('failed', (message) => message
25
+ .tag('failed')
26
+ .append(`Project name is required: ${colors_1.default.bold('motia create --plugin [project-name]')}\n`));
27
+ return;
28
+ }
29
+ return (0, index_1.create)({
30
+ projectName: args.name,
31
+ template: 'plugin',
32
+ cursorEnabled: false,
33
+ context,
34
+ });
35
+ }
36
+ else if (!args.template) {
21
37
  questions.push({
22
38
  type: 'list',
23
39
  name: 'template',
@@ -0,0 +1,15 @@
1
+ [
2
+ {
3
+ "id": "hello-world-flow",
4
+ "config": {
5
+ "steps/hello/process-greeting.step.ts": {
6
+ "x": 451,
7
+ "y": -24
8
+ },
9
+ "steps/hello/hello-api.step.ts": {
10
+ "x": -37,
11
+ "y": -68
12
+ }
13
+ }
14
+ }
15
+ ]
@@ -0,0 +1,9 @@
1
+ import { config } from '@motiadev/core'
2
+ const statesPlugin = require('@motiadev/plugin-states/plugin')
3
+ const endpointPlugin = require('@motiadev/plugin-endpoint/plugin')
4
+ const logsPlugin = require('@motiadev/plugin-logs/plugin')
5
+ const observabilityPlugin = require('@motiadev/plugin-observability/plugin')
6
+
7
+ export default config({
8
+ plugins: [observabilityPlugin, statesPlugin, endpointPlugin, logsPlugin],
9
+ })
@@ -0,0 +1,46 @@
1
+ import type { ApiRouteConfig, Handlers } from 'motia';
2
+ import { z } from 'zod';
3
+
4
+ export const config: ApiRouteConfig = {
5
+ name: 'HelloAPI',
6
+ type: 'api',
7
+ path: '/hello',
8
+ method: 'GET',
9
+ description: 'Receives hello request and emits event for processing',
10
+ emits: ['process-greeting'],
11
+ flows: ['hello-world-flow'],
12
+ responseSchema: {
13
+ 200: z.object({
14
+ message: z.string(),
15
+ status: z.string(),
16
+ appName: z.string()
17
+ })
18
+ }
19
+ };
20
+
21
+ export const handler: Handlers['HelloAPI'] = async (_, { emit, logger }) => {
22
+ const appName = process.env.APP_NAME || 'Motia App';
23
+ const timestamp = new Date().toISOString();
24
+
25
+ logger.info('Hello API endpoint called', { appName, timestamp });
26
+
27
+ // Emit event for background processing
28
+ await emit({
29
+ topic: 'process-greeting',
30
+ data: {
31
+ timestamp,
32
+ appName,
33
+ greetingPrefix: process.env.GREETING_PREFIX || 'Hello',
34
+ requestId: Math.random().toString(36).substring(7)
35
+ }
36
+ });
37
+
38
+ return {
39
+ status: 200,
40
+ body: {
41
+ message: 'Hello request received! Check logs for processing.',
42
+ status: 'processing',
43
+ appName
44
+ }
45
+ };
46
+ };
@@ -0,0 +1,43 @@
1
+ import type { EventConfig, Handlers } from 'motia';
2
+ import { z } from 'zod';
3
+
4
+ const inputSchema = z.object({
5
+ timestamp: z.string(),
6
+ appName: z.string(),
7
+ greetingPrefix: z.string(),
8
+ requestId: z.string()
9
+ });
10
+
11
+ export const config: EventConfig = {
12
+ name: 'ProcessGreeting',
13
+ type: 'event',
14
+ description: 'Processes greeting in the background',
15
+ subscribes: ['process-greeting'],
16
+ emits: [],
17
+ flows: ['hello-world-flow'],
18
+ input: inputSchema
19
+ };
20
+
21
+ export const handler: Handlers['ProcessGreeting'] = async (input, { logger, state }) => {
22
+ const { timestamp, appName, greetingPrefix, requestId } = input;
23
+
24
+ logger.info('Processing greeting', { requestId, appName });
25
+
26
+ const greeting = `${greetingPrefix} ${appName}!`;
27
+
28
+ await new Promise(resolve => setTimeout(resolve, 2000)); // Simulate background processing
29
+
30
+ // Store result in state (demonstrates state usage)
31
+ // Note: The state.set method takes (groupId, key, value)
32
+ await state.set('greetings', requestId, {
33
+ greeting,
34
+ processedAt: new Date().toISOString(),
35
+ originalTimestamp: timestamp
36
+ });
37
+
38
+ logger.info('Greeting processed successfully', {
39
+ requestId,
40
+ greeting,
41
+ storedInState: true
42
+ });
43
+ };
@@ -0,0 +1,13 @@
1
+ {
2
+ "id": "hello-world-flow",
3
+ "config": {
4
+ "steps/hello/process-greeting.step.js": {
5
+ "x": 451,
6
+ "y": -24
7
+ },
8
+ "steps/hello/hello-api.step.js": {
9
+ "x": -37,
10
+ "y": -68
11
+ }
12
+ }
13
+ }
@@ -0,0 +1,9 @@
1
+ import { config } from '@motiadev/core'
2
+ const statesPlugin = require('@motiadev/plugin-states/plugin')
3
+ const endpointPlugin = require('@motiadev/plugin-endpoint/plugin')
4
+ const logsPlugin = require('@motiadev/plugin-logs/plugin')
5
+ const observabilityPlugin = require('@motiadev/plugin-observability/plugin')
6
+
7
+ export default config({
8
+ plugins: [observabilityPlugin, statesPlugin, endpointPlugin, logsPlugin],
9
+ })
@@ -0,0 +1,47 @@
1
+ const { z } = require('zod');
2
+
3
+ const config = {
4
+ name: 'HelloAPI',
5
+ type: 'api',
6
+ path: '/hello',
7
+ method: 'GET',
8
+ description: 'Receives hello request and emits event for processing',
9
+ emits: ['process-greeting'],
10
+ flows: ['hello-world-flow'],
11
+ responseSchema: {
12
+ 200: z.object({
13
+ message: z.string(),
14
+ status: z.string(),
15
+ appName: z.string()
16
+ })
17
+ }
18
+ };
19
+
20
+ const handler = async (_, { emit, logger }) => {
21
+ const appName = process.env.APP_NAME || 'Motia App';
22
+ const timestamp = new Date().toISOString();
23
+
24
+ logger.info('Hello API endpoint called', { appName, timestamp });
25
+
26
+ // Emit event for background processing
27
+ await emit({
28
+ topic: 'process-greeting',
29
+ data: {
30
+ timestamp,
31
+ appName,
32
+ greetingPrefix: process.env.GREETING_PREFIX || 'Hello',
33
+ requestId: Math.random().toString(36).substring(7)
34
+ }
35
+ });
36
+
37
+ return {
38
+ status: 200,
39
+ body: {
40
+ message: 'Hello request received! Check logs for processing.',
41
+ status: 'processing',
42
+ appName
43
+ }
44
+ };
45
+ };
46
+
47
+ module.exports = { config, handler };
@@ -0,0 +1,44 @@
1
+ const { z } = require('zod');
2
+
3
+ const inputSchema = z.object({
4
+ timestamp: z.string(),
5
+ appName: z.string(),
6
+ greetingPrefix: z.string(),
7
+ requestId: z.string()
8
+ });
9
+
10
+ const config = {
11
+ name: 'ProcessGreeting',
12
+ type: 'event',
13
+ description: 'Processes greeting in the background',
14
+ subscribes: ['process-greeting'],
15
+ emits: [],
16
+ flows: ['hello-world-flow'],
17
+ input: inputSchema
18
+ };
19
+
20
+ const handler = async (input, { logger, state }) => {
21
+ const { timestamp, appName, greetingPrefix, requestId } = input;
22
+
23
+ logger.info('Processing greeting', { requestId, appName });
24
+
25
+ const greeting = `${greetingPrefix} ${appName}!`;
26
+
27
+ await new Promise(resolve => setTimeout(resolve, 2000)); // Simulate background processing
28
+
29
+ // Store result in state (demonstrates state usage)
30
+ // Note: The state.set method takes (groupId, key, value)
31
+ await state.set('greetings', requestId, {
32
+ greeting,
33
+ processedAt: new Date().toISOString(),
34
+ originalTimestamp: timestamp
35
+ });
36
+
37
+ logger.info('Greeting processed successfully', {
38
+ requestId,
39
+ greeting,
40
+ storedInState: true
41
+ });
42
+ };
43
+
44
+ module.exports = { config, handler };
@@ -0,0 +1,13 @@
1
+ {
2
+ "id": "hello-world-flow",
3
+ "config": {
4
+ "steps/hello/process_greeting_step.py": {
5
+ "x": 451,
6
+ "y": -24
7
+ },
8
+ "steps/hello/hello_api_step.py": {
9
+ "x": -37,
10
+ "y": -68
11
+ }
12
+ }
13
+ }
@@ -0,0 +1,9 @@
1
+ import { config } from '@motiadev/core'
2
+ const statesPlugin = require('@motiadev/plugin-states/plugin')
3
+ const endpointPlugin = require('@motiadev/plugin-endpoint/plugin')
4
+ const logsPlugin = require('@motiadev/plugin-logs/plugin')
5
+ const observabilityPlugin = require('@motiadev/plugin-observability/plugin')
6
+
7
+ export default config({
8
+ plugins: [observabilityPlugin, statesPlugin, endpointPlugin, logsPlugin],
9
+ })
@@ -0,0 +1,2 @@
1
+ # Optional: Uncomment if you want to use Pydantic for validation
2
+ # pydantic>=2.0.0
@@ -0,0 +1,74 @@
1
+ import os
2
+ import random
3
+ import string
4
+ from datetime import datetime
5
+
6
+ # Optional: Using Pydantic for validation (remove if not using Pydantic)
7
+ try:
8
+ from pydantic import BaseModel
9
+
10
+ class HelloResponse(BaseModel):
11
+ message: str
12
+ status: str
13
+ appName: str
14
+
15
+ # If using Pydantic, we can generate the JSON schema
16
+ response_schema = {
17
+ 200: HelloResponse.model_json_schema()
18
+ }
19
+ except ImportError:
20
+ # Without Pydantic, define JSON schema manually
21
+ response_schema = {
22
+ 200: {
23
+ "type": "object",
24
+ "properties": {
25
+ "message": {"type": "string"},
26
+ "status": {"type": "string"},
27
+ "appName": {"type": "string"}
28
+ },
29
+ "required": ["message", "status", "appName"]
30
+ }
31
+ }
32
+
33
+ config = {
34
+ "name": "HelloAPI",
35
+ "type": "api",
36
+ "path": "/hello",
37
+ "method": "GET",
38
+ "description": "Receives hello request and emits event for processing",
39
+ "emits": ["process-greeting"],
40
+ "flows": ["hello-world-flow"],
41
+ "responseSchema": response_schema
42
+ }
43
+
44
+ async def handler(req, context):
45
+ app_name = os.environ.get("APP_NAME", "Motia App")
46
+ timestamp = datetime.utcnow().isoformat()
47
+
48
+ context.logger.info("Hello API endpoint called", {
49
+ "app_name": app_name,
50
+ "timestamp": timestamp
51
+ })
52
+
53
+ # Generate a random request ID
54
+ request_id = ''.join(random.choices(string.ascii_lowercase + string.digits, k=7))
55
+
56
+ # Emit event for background processing
57
+ await context.emit({
58
+ "topic": "process-greeting",
59
+ "data": {
60
+ "timestamp": timestamp,
61
+ "appName": app_name,
62
+ "greetingPrefix": os.environ.get("GREETING_PREFIX", "Hello"),
63
+ "requestId": request_id
64
+ }
65
+ })
66
+
67
+ return {
68
+ "status": 200,
69
+ "body": {
70
+ "message": "Hello request received! Check logs for processing.",
71
+ "status": "processing",
72
+ "appName": app_name
73
+ }
74
+ }
@@ -0,0 +1,68 @@
1
+ import asyncio
2
+ from datetime import datetime
3
+
4
+ # Optional: Using Pydantic for validation (remove if not using Pydantic)
5
+ try:
6
+ from pydantic import BaseModel
7
+
8
+ class GreetingInput(BaseModel):
9
+ timestamp: str
10
+ appName: str
11
+ greetingPrefix: str
12
+ requestId: str
13
+
14
+ # If using Pydantic, we can generate the JSON schema
15
+ input_schema = GreetingInput.model_json_schema()
16
+ except ImportError:
17
+ # Without Pydantic, define JSON schema manually
18
+ input_schema = {
19
+ "type": "object",
20
+ "properties": {
21
+ "timestamp": {"type": "string"},
22
+ "appName": {"type": "string"},
23
+ "greetingPrefix": {"type": "string"},
24
+ "requestId": {"type": "string"}
25
+ },
26
+ "required": ["timestamp", "appName", "greetingPrefix", "requestId"]
27
+ }
28
+
29
+ config = {
30
+ "name": "ProcessGreeting",
31
+ "type": "event",
32
+ "description": "Processes greeting in the background",
33
+ "subscribes": ["process-greeting"],
34
+ "emits": [],
35
+ "flows": ["hello-world-flow"],
36
+ "input": input_schema
37
+ }
38
+
39
+ async def handler(input_data, context):
40
+ # Extract data from input
41
+ timestamp = input_data.get("timestamp")
42
+ app_name = input_data.get("appName")
43
+ greeting_prefix = input_data.get("greetingPrefix")
44
+ request_id = input_data.get("requestId")
45
+
46
+ context.logger.info("Processing greeting", {
47
+ "request_id": request_id,
48
+ "app_name": app_name
49
+ })
50
+
51
+ greeting = f"{greeting_prefix} {app_name}!"
52
+
53
+ # Simulate background processing
54
+ await asyncio.sleep(2)
55
+
56
+ # Store result in state (demonstrates state usage)
57
+ # Note: The state.set method takes (groupId, key, value)
58
+ await context.state.set("greetings", request_id, {
59
+ "greeting": greeting,
60
+ "processedAt": datetime.utcnow().isoformat(),
61
+ "originalTimestamp": timestamp
62
+ })
63
+
64
+ context.logger.info("Greeting processed successfully", {
65
+ "request_id": request_id,
66
+ "greeting": greeting,
67
+ "stored_in_state": True
68
+ })
@@ -6,4 +6,7 @@ exports.templates = {
6
6
  nodejs: (0, generate_1.generateTemplateSteps)('nodejs'),
7
7
  python: (0, generate_1.generateTemplateSteps)('python'),
8
8
  plugin: (0, generate_1.generatePluginTemplate)('plugin'),
9
+ 'starter-typescript': (0, generate_1.generateTemplateSteps)('hello'),
10
+ 'starter-javascript': (0, generate_1.generateTemplateSteps)('hello_js'),
11
+ 'starter-python': (0, generate_1.generateTemplateSteps)('hello_python'),
9
12
  };
@@ -4,4 +4,7 @@ export const templates: Record<string, Generator> = {
4
4
  nodejs: generateTemplateSteps('nodejs'),
5
5
  python: generateTemplateSteps('python'),
6
6
  plugin: generatePluginTemplate('plugin'),
7
+ 'starter-typescript': generateTemplateSteps('hello'),
8
+ 'starter-javascript': generateTemplateSteps('hello_js'),
9
+ 'starter-python': generateTemplateSteps('hello_python'),
7
10
  }
@@ -5,5 +5,5 @@ const logsPlugin = require('@motiadev/plugin-logs/plugin')
5
5
  const observabilityPlugin = require('@motiadev/plugin-observability/plugin')
6
6
 
7
7
  export default config({
8
- plugins: [statesPlugin, endpointPlugin, logsPlugin, observabilityPlugin],
8
+ plugins: [observabilityPlugin, statesPlugin, endpointPlugin, logsPlugin],
9
9
  })
@@ -5,5 +5,5 @@ const logsPlugin = require('@motiadev/plugin-logs/plugin')
5
5
  const observabilityPlugin = require('@motiadev/plugin-observability/plugin')
6
6
 
7
7
  export default config({
8
- plugins: [statesPlugin, endpointPlugin, logsPlugin, observabilityPlugin],
8
+ plugins: [observabilityPlugin, statesPlugin, endpointPlugin, logsPlugin],
9
9
  })
package/dist/esm/cli.js CHANGED
@@ -22,6 +22,7 @@ program
22
22
  .command('create [name]')
23
23
  .description('Create a new motia project')
24
24
  .option('-t, --template <template>', 'The template to use for your project')
25
+ .option('-p, --plugin', 'Create a plugin project')
25
26
  .option('-i, --interactive', 'Use interactive prompts to create project') // it's default
26
27
  .option('-c, --confirm', 'Confirm the project creation', false)
27
28
  .action((projectName, options) => {
@@ -31,6 +32,7 @@ program
31
32
  await createInteractive({
32
33
  name: arg.name,
33
34
  template: arg.template,
35
+ plugin: !!arg.plugin,
34
36
  confirm: !!arg.confirm,
35
37
  }, context);
36
38
  })(mergedArgs);
@@ -1,3 +1,5 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
1
3
  function getWorkbenchBase() {
2
4
  const basePath = process.env.MOTIA_WORKBENCH_BASE;
3
5
  if (basePath === '/') {
@@ -6,4 +8,4 @@ function getWorkbenchBase() {
6
8
  return basePath && basePath[0] !== '/' ? `/${basePath}` : basePath || '';
7
9
  }
8
10
  export const workbenchBase = getWorkbenchBase();
9
- export const isTutorialDisabled = process.env.MOTIA_TUTORIAL_DISABLED === 'true';
11
+ export const isTutorialDisabled = process.env.MOTIA_TUTORIAL_DISABLED === 'true' || !fs.existsSync(path.join(process.cwd(), 'tutorial/tutorial.tsx'));
@@ -2,6 +2,7 @@ import type { CliContext } from '../cloud/config-utils';
2
2
  interface CreateInteractiveArgs {
3
3
  name?: string;
4
4
  template?: string;
5
+ plugin?: boolean;
5
6
  confirm?: boolean;
6
7
  }
7
8
  export declare const createInteractive: (args: CreateInteractiveArgs, context: CliContext) => Promise<void>;
@@ -2,16 +2,32 @@ import colors from 'colors';
2
2
  import inquirer from 'inquirer';
3
3
  import { create } from './index';
4
4
  const choices = {
5
- nodejs: 'Base (TypeScript)',
6
- python: 'Base (Python)',
7
- plugin: 'Plugin (TypeScript)',
5
+ nodejs: 'Tutorial (TypeScript)',
6
+ python: 'Tutorial (Python)',
7
+ 'starter-typescript': 'Starter (TypeScript)',
8
+ 'starter-javascript': 'Starter (JavaScript)',
9
+ 'starter-python': 'Starter (Python)',
8
10
  };
9
11
  export const createInteractive = async (args, context) => {
10
- context.log('welcome', (message) => message.append('\n🚀 ' + colors.bold('Welcome to Motia Project Creator!')));
12
+ context.log('welcome', (message) => message.append(`\n🚀 ${colors.bold(args.plugin ? 'Welcome to Motia Plugin Creator!' : 'Welcome to Motia Project Creator!')}`));
11
13
  const questions = [];
12
14
  let name = args.name;
13
15
  let template = args.template;
14
- if (!args.template) {
16
+ if (args.plugin) {
17
+ if (!args.name) {
18
+ context.log('failed', (message) => message
19
+ .tag('failed')
20
+ .append(`Project name is required: ${colors.bold('motia create --plugin [project-name]')}\n`));
21
+ return;
22
+ }
23
+ return create({
24
+ projectName: args.name,
25
+ template: 'plugin',
26
+ cursorEnabled: false,
27
+ context,
28
+ });
29
+ }
30
+ else if (!args.template) {
15
31
  questions.push({
16
32
  type: 'list',
17
33
  name: 'template',
@@ -0,0 +1,15 @@
1
+ [
2
+ {
3
+ "id": "hello-world-flow",
4
+ "config": {
5
+ "steps/hello/process-greeting.step.ts": {
6
+ "x": 451,
7
+ "y": -24
8
+ },
9
+ "steps/hello/hello-api.step.ts": {
10
+ "x": -37,
11
+ "y": -68
12
+ }
13
+ }
14
+ }
15
+ ]
@@ -0,0 +1,9 @@
1
+ import { config } from '@motiadev/core'
2
+ const statesPlugin = require('@motiadev/plugin-states/plugin')
3
+ const endpointPlugin = require('@motiadev/plugin-endpoint/plugin')
4
+ const logsPlugin = require('@motiadev/plugin-logs/plugin')
5
+ const observabilityPlugin = require('@motiadev/plugin-observability/plugin')
6
+
7
+ export default config({
8
+ plugins: [observabilityPlugin, statesPlugin, endpointPlugin, logsPlugin],
9
+ })
@@ -0,0 +1,46 @@
1
+ import type { ApiRouteConfig, Handlers } from 'motia';
2
+ import { z } from 'zod';
3
+
4
+ export const config: ApiRouteConfig = {
5
+ name: 'HelloAPI',
6
+ type: 'api',
7
+ path: '/hello',
8
+ method: 'GET',
9
+ description: 'Receives hello request and emits event for processing',
10
+ emits: ['process-greeting'],
11
+ flows: ['hello-world-flow'],
12
+ responseSchema: {
13
+ 200: z.object({
14
+ message: z.string(),
15
+ status: z.string(),
16
+ appName: z.string()
17
+ })
18
+ }
19
+ };
20
+
21
+ export const handler: Handlers['HelloAPI'] = async (_, { emit, logger }) => {
22
+ const appName = process.env.APP_NAME || 'Motia App';
23
+ const timestamp = new Date().toISOString();
24
+
25
+ logger.info('Hello API endpoint called', { appName, timestamp });
26
+
27
+ // Emit event for background processing
28
+ await emit({
29
+ topic: 'process-greeting',
30
+ data: {
31
+ timestamp,
32
+ appName,
33
+ greetingPrefix: process.env.GREETING_PREFIX || 'Hello',
34
+ requestId: Math.random().toString(36).substring(7)
35
+ }
36
+ });
37
+
38
+ return {
39
+ status: 200,
40
+ body: {
41
+ message: 'Hello request received! Check logs for processing.',
42
+ status: 'processing',
43
+ appName
44
+ }
45
+ };
46
+ };
@@ -0,0 +1,43 @@
1
+ import type { EventConfig, Handlers } from 'motia';
2
+ import { z } from 'zod';
3
+
4
+ const inputSchema = z.object({
5
+ timestamp: z.string(),
6
+ appName: z.string(),
7
+ greetingPrefix: z.string(),
8
+ requestId: z.string()
9
+ });
10
+
11
+ export const config: EventConfig = {
12
+ name: 'ProcessGreeting',
13
+ type: 'event',
14
+ description: 'Processes greeting in the background',
15
+ subscribes: ['process-greeting'],
16
+ emits: [],
17
+ flows: ['hello-world-flow'],
18
+ input: inputSchema
19
+ };
20
+
21
+ export const handler: Handlers['ProcessGreeting'] = async (input, { logger, state }) => {
22
+ const { timestamp, appName, greetingPrefix, requestId } = input;
23
+
24
+ logger.info('Processing greeting', { requestId, appName });
25
+
26
+ const greeting = `${greetingPrefix} ${appName}!`;
27
+
28
+ await new Promise(resolve => setTimeout(resolve, 2000)); // Simulate background processing
29
+
30
+ // Store result in state (demonstrates state usage)
31
+ // Note: The state.set method takes (groupId, key, value)
32
+ await state.set('greetings', requestId, {
33
+ greeting,
34
+ processedAt: new Date().toISOString(),
35
+ originalTimestamp: timestamp
36
+ });
37
+
38
+ logger.info('Greeting processed successfully', {
39
+ requestId,
40
+ greeting,
41
+ storedInState: true
42
+ });
43
+ };
@@ -0,0 +1,13 @@
1
+ {
2
+ "id": "hello-world-flow",
3
+ "config": {
4
+ "steps/hello/process-greeting.step.js": {
5
+ "x": 451,
6
+ "y": -24
7
+ },
8
+ "steps/hello/hello-api.step.js": {
9
+ "x": -37,
10
+ "y": -68
11
+ }
12
+ }
13
+ }
@@ -0,0 +1,9 @@
1
+ import { config } from '@motiadev/core'
2
+ const statesPlugin = require('@motiadev/plugin-states/plugin')
3
+ const endpointPlugin = require('@motiadev/plugin-endpoint/plugin')
4
+ const logsPlugin = require('@motiadev/plugin-logs/plugin')
5
+ const observabilityPlugin = require('@motiadev/plugin-observability/plugin')
6
+
7
+ export default config({
8
+ plugins: [observabilityPlugin, statesPlugin, endpointPlugin, logsPlugin],
9
+ })
@@ -0,0 +1,47 @@
1
+ const { z } = require('zod');
2
+
3
+ const config = {
4
+ name: 'HelloAPI',
5
+ type: 'api',
6
+ path: '/hello',
7
+ method: 'GET',
8
+ description: 'Receives hello request and emits event for processing',
9
+ emits: ['process-greeting'],
10
+ flows: ['hello-world-flow'],
11
+ responseSchema: {
12
+ 200: z.object({
13
+ message: z.string(),
14
+ status: z.string(),
15
+ appName: z.string()
16
+ })
17
+ }
18
+ };
19
+
20
+ const handler = async (_, { emit, logger }) => {
21
+ const appName = process.env.APP_NAME || 'Motia App';
22
+ const timestamp = new Date().toISOString();
23
+
24
+ logger.info('Hello API endpoint called', { appName, timestamp });
25
+
26
+ // Emit event for background processing
27
+ await emit({
28
+ topic: 'process-greeting',
29
+ data: {
30
+ timestamp,
31
+ appName,
32
+ greetingPrefix: process.env.GREETING_PREFIX || 'Hello',
33
+ requestId: Math.random().toString(36).substring(7)
34
+ }
35
+ });
36
+
37
+ return {
38
+ status: 200,
39
+ body: {
40
+ message: 'Hello request received! Check logs for processing.',
41
+ status: 'processing',
42
+ appName
43
+ }
44
+ };
45
+ };
46
+
47
+ module.exports = { config, handler };
@@ -0,0 +1,44 @@
1
+ const { z } = require('zod');
2
+
3
+ const inputSchema = z.object({
4
+ timestamp: z.string(),
5
+ appName: z.string(),
6
+ greetingPrefix: z.string(),
7
+ requestId: z.string()
8
+ });
9
+
10
+ const config = {
11
+ name: 'ProcessGreeting',
12
+ type: 'event',
13
+ description: 'Processes greeting in the background',
14
+ subscribes: ['process-greeting'],
15
+ emits: [],
16
+ flows: ['hello-world-flow'],
17
+ input: inputSchema
18
+ };
19
+
20
+ const handler = async (input, { logger, state }) => {
21
+ const { timestamp, appName, greetingPrefix, requestId } = input;
22
+
23
+ logger.info('Processing greeting', { requestId, appName });
24
+
25
+ const greeting = `${greetingPrefix} ${appName}!`;
26
+
27
+ await new Promise(resolve => setTimeout(resolve, 2000)); // Simulate background processing
28
+
29
+ // Store result in state (demonstrates state usage)
30
+ // Note: The state.set method takes (groupId, key, value)
31
+ await state.set('greetings', requestId, {
32
+ greeting,
33
+ processedAt: new Date().toISOString(),
34
+ originalTimestamp: timestamp
35
+ });
36
+
37
+ logger.info('Greeting processed successfully', {
38
+ requestId,
39
+ greeting,
40
+ storedInState: true
41
+ });
42
+ };
43
+
44
+ module.exports = { config, handler };
@@ -0,0 +1,13 @@
1
+ {
2
+ "id": "hello-world-flow",
3
+ "config": {
4
+ "steps/hello/process_greeting_step.py": {
5
+ "x": 451,
6
+ "y": -24
7
+ },
8
+ "steps/hello/hello_api_step.py": {
9
+ "x": -37,
10
+ "y": -68
11
+ }
12
+ }
13
+ }
@@ -0,0 +1,9 @@
1
+ import { config } from '@motiadev/core'
2
+ const statesPlugin = require('@motiadev/plugin-states/plugin')
3
+ const endpointPlugin = require('@motiadev/plugin-endpoint/plugin')
4
+ const logsPlugin = require('@motiadev/plugin-logs/plugin')
5
+ const observabilityPlugin = require('@motiadev/plugin-observability/plugin')
6
+
7
+ export default config({
8
+ plugins: [observabilityPlugin, statesPlugin, endpointPlugin, logsPlugin],
9
+ })
@@ -0,0 +1,2 @@
1
+ # Optional: Uncomment if you want to use Pydantic for validation
2
+ # pydantic>=2.0.0
@@ -0,0 +1,74 @@
1
+ import os
2
+ import random
3
+ import string
4
+ from datetime import datetime
5
+
6
+ # Optional: Using Pydantic for validation (remove if not using Pydantic)
7
+ try:
8
+ from pydantic import BaseModel
9
+
10
+ class HelloResponse(BaseModel):
11
+ message: str
12
+ status: str
13
+ appName: str
14
+
15
+ # If using Pydantic, we can generate the JSON schema
16
+ response_schema = {
17
+ 200: HelloResponse.model_json_schema()
18
+ }
19
+ except ImportError:
20
+ # Without Pydantic, define JSON schema manually
21
+ response_schema = {
22
+ 200: {
23
+ "type": "object",
24
+ "properties": {
25
+ "message": {"type": "string"},
26
+ "status": {"type": "string"},
27
+ "appName": {"type": "string"}
28
+ },
29
+ "required": ["message", "status", "appName"]
30
+ }
31
+ }
32
+
33
+ config = {
34
+ "name": "HelloAPI",
35
+ "type": "api",
36
+ "path": "/hello",
37
+ "method": "GET",
38
+ "description": "Receives hello request and emits event for processing",
39
+ "emits": ["process-greeting"],
40
+ "flows": ["hello-world-flow"],
41
+ "responseSchema": response_schema
42
+ }
43
+
44
+ async def handler(req, context):
45
+ app_name = os.environ.get("APP_NAME", "Motia App")
46
+ timestamp = datetime.utcnow().isoformat()
47
+
48
+ context.logger.info("Hello API endpoint called", {
49
+ "app_name": app_name,
50
+ "timestamp": timestamp
51
+ })
52
+
53
+ # Generate a random request ID
54
+ request_id = ''.join(random.choices(string.ascii_lowercase + string.digits, k=7))
55
+
56
+ # Emit event for background processing
57
+ await context.emit({
58
+ "topic": "process-greeting",
59
+ "data": {
60
+ "timestamp": timestamp,
61
+ "appName": app_name,
62
+ "greetingPrefix": os.environ.get("GREETING_PREFIX", "Hello"),
63
+ "requestId": request_id
64
+ }
65
+ })
66
+
67
+ return {
68
+ "status": 200,
69
+ "body": {
70
+ "message": "Hello request received! Check logs for processing.",
71
+ "status": "processing",
72
+ "appName": app_name
73
+ }
74
+ }
@@ -0,0 +1,68 @@
1
+ import asyncio
2
+ from datetime import datetime
3
+
4
+ # Optional: Using Pydantic for validation (remove if not using Pydantic)
5
+ try:
6
+ from pydantic import BaseModel
7
+
8
+ class GreetingInput(BaseModel):
9
+ timestamp: str
10
+ appName: str
11
+ greetingPrefix: str
12
+ requestId: str
13
+
14
+ # If using Pydantic, we can generate the JSON schema
15
+ input_schema = GreetingInput.model_json_schema()
16
+ except ImportError:
17
+ # Without Pydantic, define JSON schema manually
18
+ input_schema = {
19
+ "type": "object",
20
+ "properties": {
21
+ "timestamp": {"type": "string"},
22
+ "appName": {"type": "string"},
23
+ "greetingPrefix": {"type": "string"},
24
+ "requestId": {"type": "string"}
25
+ },
26
+ "required": ["timestamp", "appName", "greetingPrefix", "requestId"]
27
+ }
28
+
29
+ config = {
30
+ "name": "ProcessGreeting",
31
+ "type": "event",
32
+ "description": "Processes greeting in the background",
33
+ "subscribes": ["process-greeting"],
34
+ "emits": [],
35
+ "flows": ["hello-world-flow"],
36
+ "input": input_schema
37
+ }
38
+
39
+ async def handler(input_data, context):
40
+ # Extract data from input
41
+ timestamp = input_data.get("timestamp")
42
+ app_name = input_data.get("appName")
43
+ greeting_prefix = input_data.get("greetingPrefix")
44
+ request_id = input_data.get("requestId")
45
+
46
+ context.logger.info("Processing greeting", {
47
+ "request_id": request_id,
48
+ "app_name": app_name
49
+ })
50
+
51
+ greeting = f"{greeting_prefix} {app_name}!"
52
+
53
+ # Simulate background processing
54
+ await asyncio.sleep(2)
55
+
56
+ # Store result in state (demonstrates state usage)
57
+ # Note: The state.set method takes (groupId, key, value)
58
+ await context.state.set("greetings", request_id, {
59
+ "greeting": greeting,
60
+ "processedAt": datetime.utcnow().isoformat(),
61
+ "originalTimestamp": timestamp
62
+ })
63
+
64
+ context.logger.info("Greeting processed successfully", {
65
+ "request_id": request_id,
66
+ "greeting": greeting,
67
+ "stored_in_state": True
68
+ })
@@ -3,4 +3,7 @@ export const templates = {
3
3
  nodejs: generateTemplateSteps('nodejs'),
4
4
  python: generateTemplateSteps('python'),
5
5
  plugin: generatePluginTemplate('plugin'),
6
+ 'starter-typescript': generateTemplateSteps('hello'),
7
+ 'starter-javascript': generateTemplateSteps('hello_js'),
8
+ 'starter-python': generateTemplateSteps('hello_python'),
6
9
  };
@@ -4,4 +4,7 @@ export const templates: Record<string, Generator> = {
4
4
  nodejs: generateTemplateSteps('nodejs'),
5
5
  python: generateTemplateSteps('python'),
6
6
  plugin: generatePluginTemplate('plugin'),
7
+ 'starter-typescript': generateTemplateSteps('hello'),
8
+ 'starter-javascript': generateTemplateSteps('hello_js'),
9
+ 'starter-python': generateTemplateSteps('hello_python'),
7
10
  }
@@ -5,5 +5,5 @@ const logsPlugin = require('@motiadev/plugin-logs/plugin')
5
5
  const observabilityPlugin = require('@motiadev/plugin-observability/plugin')
6
6
 
7
7
  export default config({
8
- plugins: [statesPlugin, endpointPlugin, logsPlugin, observabilityPlugin],
8
+ plugins: [observabilityPlugin, statesPlugin, endpointPlugin, logsPlugin],
9
9
  })
@@ -5,5 +5,5 @@ const logsPlugin = require('@motiadev/plugin-logs/plugin')
5
5
  const observabilityPlugin = require('@motiadev/plugin-observability/plugin')
6
6
 
7
7
  export default config({
8
- plugins: [statesPlugin, endpointPlugin, logsPlugin, observabilityPlugin],
8
+ plugins: [observabilityPlugin, statesPlugin, endpointPlugin, logsPlugin],
9
9
  })
@@ -2,6 +2,7 @@ import type { CliContext } from '../cloud/config-utils';
2
2
  interface CreateInteractiveArgs {
3
3
  name?: string;
4
4
  template?: string;
5
+ plugin?: boolean;
5
6
  confirm?: boolean;
6
7
  }
7
8
  export declare const createInteractive: (args: CreateInteractiveArgs, context: CliContext) => Promise<void>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "motia",
3
3
  "description": "A Modern Unified Backend Framework for APIs, Events and Agents",
4
- "version": "0.8.6-beta.143",
4
+ "version": "0.8.6-beta.144-824340",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -46,9 +46,9 @@
46
46
  "python-ast": "^0.1.0",
47
47
  "table": "^6.9.0",
48
48
  "ts-node": "^10.9.2",
49
- "@motiadev/core": "0.8.6-beta.143",
50
- "@motiadev/stream-client-node": "0.8.6-beta.143",
51
- "@motiadev/workbench": "0.8.6-beta.143"
49
+ "@motiadev/core": "0.8.6-beta.144-824340",
50
+ "@motiadev/stream-client-node": "0.8.6-beta.144-824340",
51
+ "@motiadev/workbench": "0.8.6-beta.144-824340"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@amplitude/analytics-types": "^2.9.2",