lua-cli 2.5.7 → 3.0.0-alpha.1

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 (124) hide show
  1. package/dist/api/agent.api.service.d.ts +45 -0
  2. package/dist/api/agent.api.service.js +54 -0
  3. package/dist/api/job.api.service.d.ts +210 -0
  4. package/dist/api/job.api.service.js +200 -0
  5. package/dist/api/lazy-instances.d.ts +24 -0
  6. package/dist/api/lazy-instances.js +48 -0
  7. package/dist/api/postprocessor.api.service.d.ts +98 -0
  8. package/dist/api/postprocessor.api.service.js +76 -0
  9. package/dist/api/preprocessor.api.service.d.ts +98 -0
  10. package/dist/api/preprocessor.api.service.js +76 -0
  11. package/dist/api/user.data.api.service.d.ts +28 -0
  12. package/dist/api/user.data.api.service.js +51 -0
  13. package/dist/api/webhook.api.service.d.ts +151 -0
  14. package/dist/api/webhook.api.service.js +134 -0
  15. package/dist/api-exports.d.ts +156 -41
  16. package/dist/api-exports.js +182 -21
  17. package/dist/cli/command-definitions.js +149 -7
  18. package/dist/commands/compile.js +124 -5
  19. package/dist/commands/completion.d.ts +11 -0
  20. package/dist/commands/completion.js +209 -0
  21. package/dist/commands/env.d.ts +3 -2
  22. package/dist/commands/env.js +42 -17
  23. package/dist/commands/features.d.ts +16 -0
  24. package/dist/commands/features.js +352 -0
  25. package/dist/commands/index.d.ts +7 -0
  26. package/dist/commands/index.js +7 -0
  27. package/dist/commands/init.js +53 -7
  28. package/dist/commands/jobs.d.ts +20 -0
  29. package/dist/commands/jobs.js +533 -0
  30. package/dist/commands/logs.js +2 -5
  31. package/dist/commands/persona.d.ts +3 -2
  32. package/dist/commands/persona.js +43 -18
  33. package/dist/commands/postprocessors.d.ts +8 -0
  34. package/dist/commands/postprocessors.js +431 -0
  35. package/dist/commands/preprocessors.d.ts +8 -0
  36. package/dist/commands/preprocessors.js +431 -0
  37. package/dist/commands/push.d.ts +9 -13
  38. package/dist/commands/push.js +937 -69
  39. package/dist/commands/skills.d.ts +16 -0
  40. package/dist/commands/skills.js +438 -0
  41. package/dist/commands/test.d.ts +9 -18
  42. package/dist/commands/test.js +558 -82
  43. package/dist/commands/webhooks.d.ts +18 -0
  44. package/dist/commands/webhooks.js +424 -0
  45. package/dist/common/data.entry.instance.d.ts +7 -0
  46. package/dist/common/data.entry.instance.js +15 -0
  47. package/dist/common/job.instance.d.ts +77 -0
  48. package/dist/common/job.instance.js +108 -0
  49. package/dist/common/order.instance.d.ts +6 -0
  50. package/dist/common/order.instance.js +14 -0
  51. package/dist/common/product.instance.d.ts +6 -0
  52. package/dist/common/product.instance.js +14 -0
  53. package/dist/common/user.instance.d.ts +15 -0
  54. package/dist/common/user.instance.js +38 -0
  55. package/dist/config/constants.d.ts +2 -2
  56. package/dist/config/constants.js +4 -4
  57. package/dist/index.js +14 -3
  58. package/dist/interfaces/agent.d.ts +33 -1
  59. package/dist/interfaces/chat.d.ts +22 -0
  60. package/dist/interfaces/index.d.ts +10 -0
  61. package/dist/interfaces/index.js +7 -0
  62. package/dist/interfaces/jobs.d.ts +172 -0
  63. package/dist/interfaces/jobs.js +5 -0
  64. package/dist/interfaces/message.d.ts +18 -0
  65. package/dist/interfaces/message.js +1 -0
  66. package/dist/interfaces/postprocessors.d.ts +35 -0
  67. package/dist/interfaces/postprocessors.js +4 -0
  68. package/dist/interfaces/preprocessors.d.ts +35 -0
  69. package/dist/interfaces/preprocessors.js +4 -0
  70. package/dist/interfaces/webhooks.d.ts +104 -0
  71. package/dist/interfaces/webhooks.js +5 -0
  72. package/dist/types/api-contracts.d.ts +14 -0
  73. package/dist/types/api-contracts.js +0 -7
  74. package/dist/types/compile.types.d.ts +49 -0
  75. package/dist/types/index.d.ts +1 -1
  76. package/dist/types/index.js +1 -1
  77. package/dist/types/skill.d.ts +502 -0
  78. package/dist/types/skill.js +477 -0
  79. package/dist/utils/agent-management.d.ts +25 -0
  80. package/dist/utils/agent-management.js +67 -0
  81. package/dist/utils/bundling.d.ts +31 -1
  82. package/dist/utils/bundling.js +653 -10
  83. package/dist/utils/compile.d.ts +63 -0
  84. package/dist/utils/compile.js +691 -36
  85. package/dist/utils/deployment.d.ts +2 -1
  86. package/dist/utils/deployment.js +16 -2
  87. package/dist/utils/init-agent.d.ts +3 -1
  88. package/dist/utils/init-agent.js +6 -4
  89. package/dist/utils/init-prompts.d.ts +2 -1
  90. package/dist/utils/init-prompts.js +14 -9
  91. package/dist/utils/job-management.d.ts +24 -0
  92. package/dist/utils/job-management.js +264 -0
  93. package/dist/utils/postprocessor-management.d.ts +9 -0
  94. package/dist/utils/postprocessor-management.js +118 -0
  95. package/dist/utils/preprocessor-management.d.ts +9 -0
  96. package/dist/utils/preprocessor-management.js +118 -0
  97. package/dist/utils/sandbox.d.ts +61 -1
  98. package/dist/utils/sandbox.js +283 -72
  99. package/dist/utils/tool-detection.d.ts +3 -2
  100. package/dist/utils/tool-detection.js +18 -4
  101. package/dist/utils/webhook-management.d.ts +24 -0
  102. package/dist/utils/webhook-management.js +256 -0
  103. package/dist/web/app.css +152 -736
  104. package/dist/web/app.js +45 -45
  105. package/package.json +2 -2
  106. package/template/AGENT_CONFIGURATION.md +251 -0
  107. package/template/COMPLEX_JOB_EXAMPLES.md +795 -0
  108. package/template/DYNAMIC_JOB_CREATION.md +371 -0
  109. package/template/README.md +30 -2
  110. package/template/WEBHOOKS_JOBS_QUICKSTART.md +318 -0
  111. package/template/WEBHOOK_JOB_EXAMPLES.md +817 -0
  112. package/template/package.json +1 -1
  113. package/template/src/index-agent-example.ts +201 -0
  114. package/template/src/index.ts +39 -0
  115. package/template/src/jobs/AbandonedBasketProcessorJob.ts +139 -0
  116. package/template/src/jobs/DailyCleanupJob.ts +100 -0
  117. package/template/src/jobs/DataMigrationJob.ts +133 -0
  118. package/template/src/jobs/HealthCheckJob.ts +87 -0
  119. package/template/src/postprocessors/ResponseFormatter.ts +151 -0
  120. package/template/src/preprocessors/MessageFilter.ts +91 -0
  121. package/template/src/tools/GameScoreTrackerTool.ts +356 -0
  122. package/template/src/tools/SmartBasketTool.ts +188 -0
  123. package/template/src/webhooks/PaymentWebhook.ts +113 -0
  124. package/template/src/webhooks/UserEventWebhook.ts +77 -0
@@ -19,7 +19,7 @@
19
19
  "axios": "^1.6.0",
20
20
  "inquirer": "^12.9.6",
21
21
  "js-yaml": "^4.1.0",
22
- "lua-cli": "^2.5.6",
22
+ "lua-cli": "^2.5.8",
23
23
  "openai": "^5.23.0",
24
24
  "uuid": "^13.0.0",
25
25
  "zod": "^3.24.1"
@@ -0,0 +1,201 @@
1
+ // /**
2
+ // * LuaAgent Example - Simplified Agent Configuration
3
+ // *
4
+ // * This example shows the NEW, RECOMMENDED way to define your agent using LuaAgent.
5
+ // * Instead of exporting individual skills, webhooks, jobs, and processors,
6
+ // * you create one unified LuaAgent object that contains everything.
7
+ // *
8
+ // * Benefits:
9
+ // * - Simpler, cleaner code
10
+ // * - All configuration in one place
11
+ // * - Easier to understand and maintain
12
+ // * - Agent persona is synced with lua.skill.yaml automatically
13
+ // */
14
+
15
+ // import { LuaAgent, LuaSkill } from "lua-cli";
16
+ // import GetWeatherTool from "./tools/GetWeatherTool";
17
+ // import { GetUserDataTool, UpdateUserDataTool } from "./tools/UserDataTool";
18
+ // import CreatePostTool from "./tools/CreatePostTool";
19
+ // import { SearchProductsTool, CreateProductTool, UpdateProductTool, GetAllProductsTool, GetProductByIdTool, DeleteProductTool } from "./tools/ProductsTool";
20
+ // import { CreateBasketTool, GetBasketsTool, AddItemToBasketTool, RemoveItemFromBasketTool, ClearBasketTool, UpdateBasketStatusTool, UpdateBasketMetadataTool, CheckoutBasketTool, GetBasketByIdTool } from "./tools/BasketTool";
21
+ // import { CreateOrderTool, UpdateOrderStatusTool, GetOrderByIdTool, GetUserOrdersTool } from "./tools/OrderTool";
22
+ // import { CreateMovieTool, GetMoviesTool, GetMovieByIdTool, UpdateMovieTool, SearchMoviesTool, DeleteMovieTool } from "./tools/CustomDataTool";
23
+
24
+ // // ============================================================================
25
+ // // WEBHOOK EXAMPLES (Uncomment to enable)
26
+ // // ============================================================================
27
+ // // import userEventWebhook from "./webhooks/UserEventWebhook";
28
+ // // import paymentWebhook from "./webhooks/PaymentWebhook";
29
+
30
+ // // ============================================================================
31
+ // // JOB EXAMPLES (Uncomment to enable)
32
+ // // ============================================================================
33
+ // // import dailyCleanupJob from "./jobs/DailyCleanupJob";
34
+ // // import healthCheckJob from "./jobs/HealthCheckJob";
35
+
36
+ // // ============================================================================
37
+ // // PREPROCESSOR/POSTPROCESSOR EXAMPLES
38
+ // // ============================================================================
39
+ // import messageFilter from "./preprocessors/MessageFilter";
40
+ // import responseFormatter from "./postprocessors/ResponseFormatter";
41
+
42
+ // // ============================================================================
43
+ // // SKILLS
44
+ // // ============================================================================
45
+
46
+ // // Skills can be defined separately for better organization
47
+ // const generalSkill = new LuaSkill({
48
+ // name: "general-skill",
49
+ // version: "1.0.0",
50
+ // description: "General utilities including weather and post creation",
51
+ // context: "This skill provides weather information and post creation capabilities.",
52
+ // tools: [
53
+ // new GetWeatherTool(),
54
+ // new CreatePostTool()
55
+ // ]
56
+ // });
57
+
58
+ // const userDataSkill = new LuaSkill({
59
+ // name: "user-data-skill",
60
+ // version: "1.0.0",
61
+ // description: "User data management",
62
+ // context: "This skill provides user data retrieval and update capabilities.",
63
+ // tools: [
64
+ // new GetUserDataTool(),
65
+ // new UpdateUserDataTool()
66
+ // ]
67
+ // });
68
+
69
+ // const productSkill = new LuaSkill({
70
+ // name: "product-skill",
71
+ // version: "1.0.0",
72
+ // description: "Product catalog management",
73
+ // context: "This skill provides comprehensive product management capabilities.",
74
+ // tools: [
75
+ // new SearchProductsTool(),
76
+ // new GetAllProductsTool(),
77
+ // new CreateProductTool(),
78
+ // new UpdateProductTool(),
79
+ // new GetProductByIdTool(),
80
+ // new DeleteProductTool()
81
+ // ]
82
+ // });
83
+
84
+ // const basketSkill = new LuaSkill({
85
+ // name: "basket-skill",
86
+ // version: "1.0.0",
87
+ // description: "Shopping basket management",
88
+ // context: "This skill provides shopping basket operations.",
89
+ // tools: [
90
+ // new CreateBasketTool(),
91
+ // new GetBasketsTool(),
92
+ // new AddItemToBasketTool(),
93
+ // new RemoveItemFromBasketTool(),
94
+ // new ClearBasketTool(),
95
+ // new UpdateBasketStatusTool(),
96
+ // new UpdateBasketMetadataTool(),
97
+ // new CheckoutBasketTool(),
98
+ // new GetBasketByIdTool()
99
+ // ]
100
+ // });
101
+
102
+ // const orderSkill = new LuaSkill({
103
+ // name: "order-skill",
104
+ // version: "1.0.0",
105
+ // description: "Order management",
106
+ // context: "This skill provides order management capabilities.",
107
+ // tools: [
108
+ // new CreateOrderTool(),
109
+ // new UpdateOrderStatusTool(),
110
+ // new GetOrderByIdTool(),
111
+ // new GetUserOrdersTool()
112
+ // ]
113
+ // });
114
+
115
+ // const customDataSkill = new LuaSkill({
116
+ // name: "custom-data-skill",
117
+ // version: "1.0.0",
118
+ // description: "Custom data management for movies",
119
+ // context: "This skill provides custom data storage and retrieval for movies.",
120
+ // tools: [
121
+ // new CreateMovieTool(),
122
+ // new GetMoviesTool(),
123
+ // new GetMovieByIdTool(),
124
+ // new UpdateMovieTool(),
125
+ // new SearchMoviesTool(),
126
+ // new DeleteMovieTool()
127
+ // ]
128
+ // });
129
+
130
+ // // ============================================================================
131
+ // // AGENT CONFIGURATION (NEW APPROACH)
132
+ // // ============================================================================
133
+
134
+ // /**
135
+ // * This is the main agent configuration.
136
+ // * The LuaAgent consolidates everything in one place:
137
+ // * - Agent persona (behavior and personality)
138
+ // * - Skills (collections of tools)
139
+ // * - Webhooks (HTTP endpoints)
140
+ // * - Jobs (scheduled tasks)
141
+ // * - PreProcessors (run before messages reach agent)
142
+ // * - PostProcessors (run after agent generates responses)
143
+ // *
144
+ // * The compiler will:
145
+ // * 1. Detect this LuaAgent
146
+ // * 2. Extract all components (skills, webhooks, jobs, processors)
147
+ // * 3. Sync the persona with lua.skill.yaml
148
+ // * 4. Deploy everything to the Lua platform
149
+ // */
150
+ // export const agent = new LuaAgent({
151
+ // name: "my-assistant",
152
+ // persona: `You are a helpful e-commerce assistant that can help users with:
153
+ // - Weather information
154
+ // - User account management
155
+ // - Product browsing and search
156
+ // - Shopping basket operations
157
+ // - Order management
158
+ // - Movie database management
159
+
160
+ // You are friendly, helpful, and provide clear responses. When users ask about products,
161
+ // help them search and find what they need. When they want to purchase, guide them through
162
+ // the basket and checkout process.`,
163
+
164
+ // welcomeMessage: "Hello! I'm your e-commerce assistant. I can help you find products, manage your basket, and track orders. How can I help you today?",
165
+
166
+ // // Add all your skills here
167
+ // skills: [
168
+ // generalSkill,
169
+ // userDataSkill,
170
+ // productSkill,
171
+ // basketSkill,
172
+ // orderSkill,
173
+ // customDataSkill
174
+ // ],
175
+
176
+ // // Uncomment to add webhooks
177
+ // // webhooks: [
178
+ // // userEventWebhook,
179
+ // // paymentWebhook
180
+ // // ],
181
+
182
+ // // Uncomment to add scheduled jobs
183
+ // // jobs: [
184
+ // // dailyCleanupJob,
185
+ // // healthCheckJob
186
+ // // ],
187
+
188
+ // // Message preprocessors (filter spam, add context, etc.)
189
+ // preProcessors: [
190
+ // messageFilter
191
+ // ],
192
+
193
+ // // Response postprocessors (format output, add branding, etc.)
194
+ // postProcessors: [
195
+ // responseFormatter
196
+ // ]
197
+ // });
198
+
199
+ // // That's it! The compiler will detect this LuaAgent and deploy everything.
200
+ // // No need to export individual skills, webhooks, jobs, etc.
201
+
@@ -8,6 +8,45 @@ import { CreateBasketTool, GetBasketsTool, AddItemToBasketTool, RemoveItemFromBa
8
8
  import { CreateOrderTool, UpdateOrderStatusTool, GetOrderByIdTool, GetUserOrdersTool } from "./tools/OrderTool";
9
9
  import { CreateMovieTool, GetMoviesTool, GetMovieByIdTool, UpdateMovieTool, SearchMoviesTool, DeleteMovieTool } from "./tools/CustomDataTool";
10
10
 
11
+ // ============================================================================
12
+ // WEBHOOK EXAMPLES (Uncomment to enable)
13
+ // ============================================================================
14
+ // Webhooks receive HTTP requests from external systems
15
+ // See: WEBHOOK_JOB_EXAMPLES.md for detailed explanations
16
+
17
+ // import userEventWebhook from "./webhooks/UserEventWebhook";
18
+ // import paymentWebhook from "./webhooks/PaymentWebhook";
19
+
20
+ // ============================================================================
21
+ // JOB EXAMPLES (Uncomment to enable)
22
+ // ============================================================================
23
+ // Jobs are scheduled tasks that run at specific times or intervals
24
+ // See: WEBHOOK_JOB_EXAMPLES.md for detailed explanations
25
+
26
+ // import dailyCleanupJob from "./jobs/DailyCleanupJob";
27
+ // import healthCheckJob from "./jobs/HealthCheckJob";
28
+ // import dataMigrationJob from "./jobs/DataMigrationJob";
29
+ // import abandonedBasketProcessorJob from "./jobs/AbandonedBasketProcessorJob";
30
+
31
+ // ============================================================================
32
+ // SMART BASKET TOOL (Uncomment to enable)
33
+ // ============================================================================
34
+ // This tool creates baskets with automatic abandoned cart reminders
35
+ // Uses Jobs.create() to schedule one-time check 3 hours after creation
36
+
37
+ // import { CreateSmartBasketTool, CheckAbandonedBasketsTool } from "./tools/SmartBasketTool";
38
+
39
+ // ============================================================================
40
+ // GAME SCORE TRACKER (Uncomment to enable) - ADVANCED EXAMPLE
41
+ // ============================================================================
42
+ // Complex example showing:
43
+ // - Interval jobs that start at specific time
44
+ // - Jobs that stop themselves when condition is met
45
+ // - Real-time monitoring with external API integration
46
+ // See: COMPLEX_JOB_EXAMPLES.md for detailed explanation
47
+
48
+ // import { TrackGameScoresTool, GetGameScoresTool, StopGameTrackingTool } from "./tools/GameScoreTrackerTool";
49
+
11
50
 
12
51
  // Initialize skill with tools
13
52
  const generalSkill = new LuaSkill({
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Abandoned Basket Processor Job
3
+ *
4
+ * This job runs every 15 minutes to process basket checkout reminders.
5
+ * Works together with CreateSmartBasketTool to send abandoned cart notifications.
6
+ *
7
+ * Flow:
8
+ * 1. User creates basket → Tool schedules reminder in custom data
9
+ * 2. This job runs every 15 minutes
10
+ * 3. Checks which reminders are due
11
+ * 4. Checks if baskets were checked out
12
+ * 5. Sends reminders for abandoned baskets
13
+ */
14
+
15
+ import { LuaJob, Data, Baskets } from "lua-cli";
16
+
17
+ const abandonedBasketProcessorJob = new LuaJob({
18
+ name: "process-basket-reminders",
19
+ version: "1.0.0",
20
+ description: "Processes abandoned basket reminders",
21
+ context: "Runs every 15 minutes to check for abandoned baskets and send recovery emails. " +
22
+ "Works with basket creation tools that schedule reminders.",
23
+
24
+ // Run every 15 minutes
25
+ schedule: {
26
+ type: 'interval',
27
+ seconds: 900 // 15 minutes = 900 seconds
28
+ },
29
+
30
+ timeout: 120, // 2 minutes
31
+
32
+ execute: async () => {
33
+ console.log('🔍 Processing basket reminders...');
34
+
35
+ // Get all pending reminders
36
+ const remindersResponse = await Data.get('basket-reminders', {}, 1, 500);
37
+ const reminders = remindersResponse.data || [];
38
+ const now = new Date();
39
+
40
+ let processedCount = 0;
41
+ let abandonedCount = 0;
42
+ let checkedOutCount = 0;
43
+
44
+ for (const reminder of reminders) {
45
+ // Skip if not yet time
46
+ const scheduledTime = new Date(reminder.data.scheduledFor);
47
+ if (scheduledTime > now) continue;
48
+
49
+ // Skip if already processed
50
+ if (reminder.data.status !== 'pending') continue;
51
+
52
+ try {
53
+ // Get the basket
54
+ const basketInstance = await Baskets.getById(reminder.data.basketId);
55
+ const basket = await basketInstance.getData();
56
+
57
+ // Check basket status
58
+ const isAbandoned = basket.status === 'active';
59
+
60
+ if (isAbandoned) {
61
+ // Basket is abandoned - send reminder
62
+ console.log(`🛒 Abandoned basket found: ${basket.id}`);
63
+ abandonedCount++;
64
+
65
+ // Calculate basket value
66
+ const totalValue = basket.items?.reduce((sum: number, item: any) => {
67
+ return sum + (item.price * item.quantity);
68
+ }, 0) || 0;
69
+
70
+ // Log abandoned basket
71
+ await Data.create('abandoned-baskets', {
72
+ basketId: basket.id,
73
+ currency: basket.currency,
74
+ itemCount: basket.items?.length || 0,
75
+ totalValue,
76
+ items: basket.items,
77
+ abandonedAt: new Date().toISOString(),
78
+ reminderSent: true
79
+ });
80
+
81
+ // Update reminder status
82
+ await Data.update('basket-reminders', reminder.id, {
83
+ ...reminder.data,
84
+ status: 'completed',
85
+ result: 'abandoned',
86
+ processedAt: new Date().toISOString()
87
+ });
88
+
89
+ // TODO: Send actual reminder email/SMS
90
+ console.log(`📧 Would send reminder: "You left ${basket.items?.length || 0} items in your cart!"`);
91
+
92
+ } else {
93
+ // Basket was checked out
94
+ console.log(`✅ Basket ${basket.id} was checked out (${basket.status})`);
95
+ checkedOutCount++;
96
+
97
+ // Update reminder status
98
+ await Data.update('basket-reminders', reminder.id, {
99
+ ...reminder.data,
100
+ status: 'completed',
101
+ result: 'checked-out',
102
+ processedAt: new Date().toISOString()
103
+ });
104
+ }
105
+
106
+ processedCount++;
107
+
108
+ } catch (error) {
109
+ console.error(`❌ Error processing basket ${reminder.data.basketId}:`, error);
110
+
111
+ // Mark as error
112
+ try {
113
+ await Data.update('basket-reminders', reminder.id, {
114
+ ...reminder.data,
115
+ status: 'error',
116
+ error: String(error),
117
+ processedAt: new Date().toISOString()
118
+ });
119
+ } catch (updateError) {
120
+ console.error('Failed to update reminder status:', updateError);
121
+ }
122
+ }
123
+ }
124
+
125
+ console.log('✅ Reminder processing complete');
126
+ console.log(`📊 Processed: ${processedCount}, Abandoned: ${abandonedCount}, Checked out: ${checkedOutCount}`);
127
+
128
+ return {
129
+ success: true,
130
+ processedCount,
131
+ abandonedCount,
132
+ checkedOutCount,
133
+ timestamp: new Date().toISOString()
134
+ };
135
+ }
136
+ });
137
+
138
+ export default abandonedBasketProcessorJob;
139
+
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Daily Cleanup Job Example
3
+ *
4
+ * This job runs daily at 2 AM to clean up old data and maintain database performance.
5
+ * Demonstrates: Cron scheduling, data cleanup, and error handling.
6
+ */
7
+
8
+ import { LuaJob, Data } from "lua-cli";
9
+
10
+ const dailyCleanupJob = new LuaJob({
11
+ name: "daily-cleanup",
12
+ version: "1.0.0",
13
+ description: "Daily database cleanup and maintenance",
14
+ context: "Runs at 2 AM EST daily to remove old logs, expired sessions, and optimize data collections. " +
15
+ "This job helps maintain database performance and storage costs.",
16
+
17
+ // Cron schedule: Every day at 2 AM EST
18
+ schedule: {
19
+ type: 'cron',
20
+ expression: '0 2 * * *', // minute hour day month day-of-week
21
+ timezone: 'America/New_York'
22
+ },
23
+
24
+ // Give it 10 minutes to complete
25
+ timeout: 600,
26
+
27
+ // Retry up to 3 times if it fails
28
+ retry: {
29
+ maxAttempts: 3,
30
+ backoffSeconds: 60 // Wait 60s between retries
31
+ },
32
+
33
+ execute: async () => {
34
+ console.log('🧹 Starting daily cleanup...');
35
+ const startTime = Date.now();
36
+
37
+ let totalRecordsDeleted = 0;
38
+
39
+ // 1. Clean up old user events (older than 90 days)
40
+ const ninetyDaysAgo = new Date(Date.now() - 90 * 24 * 60 * 60 * 1000);
41
+
42
+ try {
43
+ const oldEventsResponse = await Data.get('user-events', {}, 1, 1000);
44
+ const oldEvents = oldEventsResponse.data || [];
45
+
46
+ for (const event of oldEvents) {
47
+ const eventDate = new Date(event.data.receivedAt);
48
+ if (eventDate < ninetyDaysAgo) {
49
+ await Data.delete('user-events', event.id);
50
+ totalRecordsDeleted++;
51
+ }
52
+ }
53
+
54
+ console.log(`✅ Deleted ${totalRecordsDeleted} old user events`);
55
+ } catch (error) {
56
+ console.error('❌ Error cleaning user events:', error);
57
+ }
58
+
59
+ // 2. Clean up old payment logs (older than 180 days)
60
+ const oneEightyDaysAgo = new Date(Date.now() - 180 * 24 * 60 * 60 * 1000);
61
+
62
+ try {
63
+ const oldLogsResponse = await Data.get('payment-logs', {}, 1, 1000);
64
+ const oldLogs = oldLogsResponse.data || [];
65
+ let logsDeleted = 0;
66
+
67
+ for (const log of oldLogs) {
68
+ const logDate = new Date(log.data.receivedAt);
69
+ if (logDate < oneEightyDaysAgo) {
70
+ await Data.delete('payment-logs', log.id);
71
+ logsDeleted++;
72
+ totalRecordsDeleted++;
73
+ }
74
+ }
75
+
76
+ console.log(`✅ Deleted ${logsDeleted} old payment logs`);
77
+ } catch (error) {
78
+ console.error('❌ Error cleaning payment logs:', error);
79
+ }
80
+
81
+ // 3. Clean up abandoned baskets (older than 7 days)
82
+ // Note: This would require basket API support for deletion
83
+ // For now, we'll just log the count
84
+
85
+ const duration = Date.now() - startTime;
86
+
87
+ console.log(`✅ Cleanup complete in ${duration}ms`);
88
+
89
+ return {
90
+ success: true,
91
+ recordsDeleted: totalRecordsDeleted,
92
+ duration: `${duration}ms`,
93
+ completedAt: new Date().toISOString(),
94
+ collections: ['user-events', 'payment-logs']
95
+ };
96
+ }
97
+ });
98
+
99
+ export default dailyCleanupJob;
100
+
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Data Migration Job Example
3
+ *
4
+ * This is a one-time job that runs at a specific date/time to migrate data.
5
+ * Demonstrates: One-time scheduling, data migration, and batch processing.
6
+ */
7
+
8
+ import { LuaJob, Data } from "lua-cli";
9
+
10
+ const dataMigrationJob = new LuaJob({
11
+ name: "migrate-user-schema",
12
+ version: "1.0.0",
13
+ description: "One-time migration to new user event schema",
14
+ context: "Scheduled one-time task to migrate user events from old schema to new schema. " +
15
+ "Runs once at the specified time and then automatically deactivates.",
16
+
17
+ // One-time schedule: Run at specific date/time
18
+ schedule: {
19
+ type: 'once',
20
+ executeAt: new Date('2025-12-31T02:00:00Z') // December 31, 2025 at 2 AM UTC
21
+ },
22
+
23
+ // Give it 30 minutes for large migration
24
+ timeout: 1800,
25
+
26
+ // Retry important migrations
27
+ retry: {
28
+ maxAttempts: 3,
29
+ backoffSeconds: 300 // 5 minutes between retries
30
+ },
31
+
32
+ execute: async () => {
33
+ console.log('🔄 Starting data migration...');
34
+ const startTime = Date.now();
35
+
36
+ let totalMigrated = 0;
37
+ let totalErrors = 0;
38
+
39
+ try {
40
+ // Get all user events with old schema
41
+ const oldEventsResponse = await Data.get('user-events', {}, 1, 1000);
42
+ const oldEvents = oldEventsResponse.data || [];
43
+
44
+ console.log(`📊 Found ${oldEvents.length} events to migrate`);
45
+
46
+ // Migrate each event
47
+ for (const event of oldEvents) {
48
+ try {
49
+ // Transform old schema to new schema
50
+ const migratedEvent = {
51
+ // New fields
52
+ eventId: event.id,
53
+ eventType: event.data.type || 'unknown',
54
+ userId: event.data.userId || event.data.user_id, // Handle both formats
55
+ email: event.data.email,
56
+ name: event.data.name,
57
+
58
+ // Preserve metadata
59
+ metadata: {
60
+ ...event.data.metadata,
61
+ migratedFrom: 'old-schema',
62
+ migratedAt: new Date().toISOString()
63
+ },
64
+
65
+ // Copy timestamps
66
+ originalTimestamp: event.data.timestamp,
67
+ receivedAt: event.data.receivedAt
68
+ };
69
+
70
+ // Create in new collection
71
+ await Data.create('user-events-v2', migratedEvent,
72
+ `${migratedEvent.eventType} ${migratedEvent.email}`
73
+ );
74
+
75
+ totalMigrated++;
76
+
77
+ // Log progress every 100 records
78
+ if (totalMigrated % 100 === 0) {
79
+ console.log(`✅ Migrated ${totalMigrated} records...`);
80
+ }
81
+
82
+ } catch (error) {
83
+ console.error(`❌ Failed to migrate event ${event.id}:`, error);
84
+ totalErrors++;
85
+ }
86
+ }
87
+
88
+ const duration = Date.now() - startTime;
89
+ const successRate = ((totalMigrated / oldEvents.length) * 100).toFixed(2);
90
+
91
+ console.log(`✅ Migration complete!`);
92
+ console.log(`📊 Stats:`);
93
+ console.log(` - Total records: ${oldEvents.length}`);
94
+ console.log(` - Migrated: ${totalMigrated}`);
95
+ console.log(` - Errors: ${totalErrors}`);
96
+ console.log(` - Success rate: ${successRate}%`);
97
+ console.log(` - Duration: ${duration}ms`);
98
+
99
+ // Store migration report
100
+ await Data.create('migration-reports', {
101
+ jobName: 'migrate-user-schema',
102
+ totalRecords: oldEvents.length,
103
+ migrated: totalMigrated,
104
+ errors: totalErrors,
105
+ successRate: parseFloat(successRate),
106
+ duration,
107
+ completedAt: new Date().toISOString()
108
+ });
109
+
110
+ return {
111
+ success: true,
112
+ totalRecords: oldEvents.length,
113
+ migrated: totalMigrated,
114
+ errors: totalErrors,
115
+ successRate: `${successRate}%`,
116
+ duration: `${duration}ms`
117
+ };
118
+
119
+ } catch (error) {
120
+ console.error('💥 Migration failed:', error);
121
+
122
+ return {
123
+ success: false,
124
+ error: String(error),
125
+ totalMigrated,
126
+ totalErrors
127
+ };
128
+ }
129
+ }
130
+ });
131
+
132
+ export default dataMigrationJob;
133
+