lua-cli 2.5.8 → 3.0.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/chat.api.service.d.ts +8 -0
- package/dist/api/chat.api.service.js +50 -0
- package/dist/api/job.api.service.d.ts +219 -0
- package/dist/api/job.api.service.js +216 -0
- package/dist/api/lazy-instances.d.ts +24 -0
- package/dist/api/lazy-instances.js +48 -0
- package/dist/api/postprocessor.api.service.d.ts +158 -0
- package/dist/api/postprocessor.api.service.js +111 -0
- package/dist/api/preprocessor.api.service.d.ts +158 -0
- package/dist/api/preprocessor.api.service.js +111 -0
- package/dist/api/user.data.api.service.d.ts +13 -0
- package/dist/api/user.data.api.service.js +20 -0
- package/dist/api/webhook.api.service.d.ts +151 -0
- package/dist/api/webhook.api.service.js +134 -0
- package/dist/api-exports.d.ts +176 -41
- package/dist/api-exports.js +195 -21
- package/dist/cli/command-definitions.js +85 -8
- package/dist/commands/chat.js +73 -36
- package/dist/commands/compile.js +140 -7
- package/dist/commands/dev.js +23 -2
- package/dist/commands/index.d.ts +4 -0
- package/dist/commands/index.js +4 -0
- package/dist/commands/init.js +53 -7
- package/dist/commands/jobs.d.ts +20 -0
- package/dist/commands/jobs.js +533 -0
- package/dist/commands/logs.js +2 -5
- package/dist/commands/postprocessors.d.ts +8 -0
- package/dist/commands/postprocessors.js +431 -0
- package/dist/commands/preprocessors.d.ts +8 -0
- package/dist/commands/preprocessors.js +431 -0
- package/dist/commands/push.d.ts +3 -2
- package/dist/commands/push.js +1216 -7
- package/dist/commands/test.d.ts +9 -18
- package/dist/commands/test.js +574 -82
- package/dist/commands/webhooks.d.ts +18 -0
- package/dist/commands/webhooks.js +424 -0
- package/dist/common/job.instance.d.ts +80 -0
- package/dist/common/job.instance.js +116 -0
- package/dist/common/user.instance.d.ts +1 -0
- package/dist/common/user.instance.js +9 -0
- package/dist/config/constants.d.ts +4 -3
- package/dist/config/constants.js +10 -8
- package/dist/interfaces/agent.d.ts +2 -1
- package/dist/interfaces/chat.d.ts +52 -1
- package/dist/interfaces/index.d.ts +10 -0
- package/dist/interfaces/index.js +7 -0
- package/dist/interfaces/jobs.d.ts +193 -0
- package/dist/interfaces/jobs.js +5 -0
- package/dist/interfaces/postprocessors.d.ts +35 -0
- package/dist/interfaces/postprocessors.js +4 -0
- package/dist/interfaces/preprocessors.d.ts +35 -0
- package/dist/interfaces/preprocessors.js +4 -0
- package/dist/interfaces/webhooks.d.ts +104 -0
- package/dist/interfaces/webhooks.js +5 -0
- package/dist/services/auth.d.ts +8 -2
- package/dist/services/auth.js +35 -3
- package/dist/types/api-contracts.d.ts +5 -0
- package/dist/types/compile.types.d.ts +49 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/skill.d.ts +521 -0
- package/dist/types/skill.js +471 -0
- package/dist/utils/agent-management.d.ts +25 -0
- package/dist/utils/agent-management.js +67 -0
- package/dist/utils/bundling.d.ts +44 -5
- package/dist/utils/bundling.js +723 -23
- package/dist/utils/compile.d.ts +63 -0
- package/dist/utils/compile.js +712 -36
- package/dist/utils/deployment.d.ts +2 -1
- package/dist/utils/deployment.js +16 -2
- package/dist/utils/dev-api.d.ts +42 -2
- package/dist/utils/dev-api.js +177 -4
- package/dist/utils/dev-server.d.ts +1 -1
- package/dist/utils/dev-server.js +4 -4
- package/dist/utils/dynamic-job-bundler.d.ts +17 -0
- package/dist/utils/dynamic-job-bundler.js +143 -0
- package/dist/utils/init-agent.d.ts +3 -1
- package/dist/utils/init-agent.js +6 -4
- package/dist/utils/init-prompts.d.ts +2 -1
- package/dist/utils/init-prompts.js +14 -9
- package/dist/utils/job-management.d.ts +24 -0
- package/dist/utils/job-management.js +264 -0
- package/dist/utils/postprocessor-management.d.ts +9 -0
- package/dist/utils/postprocessor-management.js +118 -0
- package/dist/utils/pre-bundle-jobs.d.ts +26 -0
- package/dist/utils/pre-bundle-jobs.js +176 -0
- package/dist/utils/preprocessor-management.d.ts +9 -0
- package/dist/utils/preprocessor-management.js +118 -0
- package/dist/utils/sandbox-storage.d.ts +48 -0
- package/dist/utils/sandbox-storage.js +114 -0
- package/dist/utils/sandbox.d.ts +61 -1
- package/dist/utils/sandbox.js +299 -72
- package/dist/utils/tool-detection.d.ts +3 -2
- package/dist/utils/tool-detection.js +18 -4
- package/dist/utils/webhook-management.d.ts +24 -0
- package/dist/utils/webhook-management.js +256 -0
- package/package.json +1 -1
- package/template/README.md +30 -2
- package/template/env.example +5 -0
- package/template/lua.skill.yaml +47 -0
- package/template/package-lock.json +10505 -0
- package/template/package.json +2 -1
- package/template/src/index.ts +103 -2
- package/template/src/jobs/AbandonedBasketProcessorJob.ts +139 -0
- package/template/src/jobs/DailyCleanupJob.ts +100 -0
- package/template/src/jobs/DataMigrationJob.ts +133 -0
- package/template/src/jobs/HealthCheckJob.ts +87 -0
- package/template/src/tools/CreateInlineJob.ts +42 -0
- package/template/src/tools/GameScoreTrackerTool.ts +356 -0
- package/template/src/tools/SmartBasketTool.ts +188 -0
- package/template/src/webhooks/PaymentWebhook.ts +113 -0
- package/template/src/webhooks/UserEventWebhook.ts +77 -0
- package/API_REFERENCE.md +0 -1408
- package/CHANGELOG.md +0 -236
- package/CLI_REFERENCE.md +0 -908
- package/GETTING_STARTED.md +0 -1040
- package/INSTANCE_TYPES.md +0 -1158
- package/README.md +0 -865
- package/TEMPLATE_GUIDE.md +0 -1398
- package/USER_DATA_INSTANCE.md +0 -621
- package/template/TOOL_EXAMPLES.md +0 -655
package/template/package.json
CHANGED
package/template/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LuaSkill, User, Products, Orders, Data, Baskets } from "lua-cli";
|
|
1
|
+
import { LuaSkill, User, Products, Orders, Data, Baskets, LuaJob, JobInstance, LuaWebhook, Jobs } from "lua-cli";
|
|
2
2
|
import GetWeatherTool from "./tools/GetWeatherTool";
|
|
3
3
|
import { GetUserDataTool, UpdateUserDataTool } from "./tools/UserDataTool";
|
|
4
4
|
import CreatePostTool from "./tools/CreatePostTool";
|
|
@@ -7,6 +7,47 @@ import CreatePaymentLinkTool from "./tools/PaymentTool";
|
|
|
7
7
|
import { CreateBasketTool, GetBasketsTool, AddItemToBasketTool, RemoveItemFromBasketTool, ClearBasketTool, UpdateBasketStatusTool, UpdateBasketMetadataTool, CheckoutBasketTool, GetBasketByIdTool } from "./tools/BasketTool";
|
|
8
8
|
import { CreateOrderTool, UpdateOrderStatusTool, GetOrderByIdTool, GetUserOrdersTool } from "./tools/OrderTool";
|
|
9
9
|
import { CreateMovieTool, GetMoviesTool, GetMovieByIdTool, UpdateMovieTool, SearchMoviesTool, DeleteMovieTool } from "./tools/CustomDataTool";
|
|
10
|
+
import CreateInlineJobTool from "./tools/CreateInlineJob";
|
|
11
|
+
import { v4 } from 'uuid';
|
|
12
|
+
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// WEBHOOK EXAMPLES (Uncomment to enable)
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Webhooks receive HTTP requests from external systems
|
|
17
|
+
// See: WEBHOOK_JOB_EXAMPLES.md for detailed explanations
|
|
18
|
+
|
|
19
|
+
// import userEventWebhook from "./webhooks/UserEventWebhook";
|
|
20
|
+
// import paymentWebhook from "./webhooks/PaymentWebhook";
|
|
21
|
+
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// JOB EXAMPLES (Uncomment to enable)
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// Jobs are scheduled tasks that run at specific times or intervals
|
|
26
|
+
// See: WEBHOOK_JOB_EXAMPLES.md for detailed explanations
|
|
27
|
+
|
|
28
|
+
import dailyCleanupJob from "./jobs/DailyCleanupJob";
|
|
29
|
+
// import healthCheckJob from "./jobs/HealthCheckJob";
|
|
30
|
+
// import dataMigrationJob from "./jobs/DataMigrationJob";
|
|
31
|
+
// import abandonedBasketProcessorJob from "./jobs/AbandonedBasketProcessorJob";
|
|
32
|
+
|
|
33
|
+
// ============================================================================
|
|
34
|
+
// SMART BASKET TOOL (Uncomment to enable)
|
|
35
|
+
// ============================================================================
|
|
36
|
+
// This tool creates baskets with automatic abandoned cart reminders
|
|
37
|
+
// Uses Jobs.create() to schedule one-time check 3 hours after creation
|
|
38
|
+
|
|
39
|
+
// import { CreateSmartBasketTool, CheckAbandonedBasketsTool } from "./tools/SmartBasketTool";
|
|
40
|
+
|
|
41
|
+
// ============================================================================
|
|
42
|
+
// GAME SCORE TRACKER (Uncomment to enable) - ADVANCED EXAMPLE
|
|
43
|
+
// ============================================================================
|
|
44
|
+
// Complex example showing:
|
|
45
|
+
// - Interval jobs that start at specific time
|
|
46
|
+
// - Jobs that stop themselves when condition is met
|
|
47
|
+
// - Real-time monitoring with external API integration
|
|
48
|
+
// See: COMPLEX_JOB_EXAMPLES.md for detailed explanation
|
|
49
|
+
|
|
50
|
+
// import { TrackGameScoresTool, GetGameScoresTool, StopGameTrackingTool } from "./tools/GameScoreTrackerTool";
|
|
10
51
|
|
|
11
52
|
|
|
12
53
|
// Initialize skill with tools
|
|
@@ -17,7 +58,8 @@ const generalSkill = new LuaSkill({
|
|
|
17
58
|
context: "This skill provides various utilities including weather information, user data retrieval, post creation, basic calculator operations, and advanced mathematical functions. Use get_weather to fetch current weather conditions for any city, get_user_data to retrieve user information, create_post to publish new posts, calculator for basic arithmetic operations, and advanced_math for complex mathematical computations like factorials, prime checking, fibonacci sequences, and greatest common divisor calculations.",
|
|
18
59
|
tools: [
|
|
19
60
|
new GetWeatherTool(),
|
|
20
|
-
new CreatePostTool()
|
|
61
|
+
new CreatePostTool(),
|
|
62
|
+
new CreateInlineJobTool()
|
|
21
63
|
]
|
|
22
64
|
});
|
|
23
65
|
|
|
@@ -120,6 +162,65 @@ const testCases = [
|
|
|
120
162
|
{ tool: "get_weather", cityLong: "London", apiKey: "123" }
|
|
121
163
|
];
|
|
122
164
|
|
|
165
|
+
const job = new LuaJob({
|
|
166
|
+
description: "Test job",
|
|
167
|
+
context: "Test job context",
|
|
168
|
+
name: "test-job",
|
|
169
|
+
schedule: {
|
|
170
|
+
type: "once",
|
|
171
|
+
executeAt: new Date(Date.now() + 1000).toISOString()
|
|
172
|
+
},
|
|
173
|
+
metadata: {
|
|
174
|
+
test: "test"
|
|
175
|
+
},
|
|
176
|
+
execute: async (job: JobInstance) => {
|
|
177
|
+
console.log("Job executed");
|
|
178
|
+
return { success: true, message: "Job executed successfully" };
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
//create webhook
|
|
183
|
+
const webhook = new LuaWebhook({
|
|
184
|
+
description: "Test webhook",
|
|
185
|
+
context: "Test webhook context",
|
|
186
|
+
name: "test-webhook",
|
|
187
|
+
execute: async (query: any, headers: any, body: any) => {
|
|
188
|
+
console.log("Webhook executed");
|
|
189
|
+
return { success: true, message: "Webhook executed successfully" };
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const newJob = new LuaJob({
|
|
194
|
+
description: "Test job",
|
|
195
|
+
context: "Test job context",
|
|
196
|
+
name: "test-job",
|
|
197
|
+
schedule: {
|
|
198
|
+
type: "once",
|
|
199
|
+
executeAt: new Date(Date.now() + 1000).toISOString()
|
|
200
|
+
},
|
|
201
|
+
execute: async (job: JobInstance) => {
|
|
202
|
+
console.log("Job executed", job);
|
|
203
|
+
const realtimeJob = await Jobs.create({
|
|
204
|
+
name: `realtime-job-new-this-again`,
|
|
205
|
+
description: "Realtime job",
|
|
206
|
+
schedule: {
|
|
207
|
+
type: "once",
|
|
208
|
+
executeAt: new Date(Date.now() + 1000).toISOString()
|
|
209
|
+
},
|
|
210
|
+
execute: async (job: JobInstance) => {
|
|
211
|
+
// console.log("Executing realtime job", job.data);
|
|
212
|
+
console.log("Realtime job metadata", job.metadata);
|
|
213
|
+
console.log("Realtime job user", job.user);
|
|
214
|
+
return { success: true, message: "Realtime job executed successfully" };
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
return { success: true, message: "Job executed successfully" };
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
console.log("Job created:", job);
|
|
223
|
+
|
|
123
224
|
async function runTests() {
|
|
124
225
|
// await seedProducts();
|
|
125
226
|
console.log("🧪 Running tool tests...\n");
|
|
@@ -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
|
+
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health Check Job Example
|
|
3
|
+
*
|
|
4
|
+
* This job runs every 5 minutes to check system health and alert on issues.
|
|
5
|
+
* Demonstrates: Interval scheduling, monitoring, and alerting.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { LuaJob, Data, Products } from "lua-cli";
|
|
9
|
+
|
|
10
|
+
const healthCheckJob = new LuaJob({
|
|
11
|
+
name: "health-check",
|
|
12
|
+
version: "1.0.0",
|
|
13
|
+
description: "System health monitoring",
|
|
14
|
+
context: "Runs every 5 minutes to check API health, database connectivity, and service availability. " +
|
|
15
|
+
"Logs health metrics and can trigger alerts if issues are detected.",
|
|
16
|
+
|
|
17
|
+
// Interval schedule: Every 5 minutes
|
|
18
|
+
schedule: {
|
|
19
|
+
type: 'interval',
|
|
20
|
+
seconds: 300 // 5 minutes = 300 seconds
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
// Quick timeout for health checks
|
|
24
|
+
timeout: 30,
|
|
25
|
+
|
|
26
|
+
execute: async () => {
|
|
27
|
+
console.log('❤️ Running health check...');
|
|
28
|
+
const checks: any = {};
|
|
29
|
+
|
|
30
|
+
// Check 1: Database connectivity
|
|
31
|
+
try {
|
|
32
|
+
const testQuery = await Data.get('health-checks', {}, 1, 1);
|
|
33
|
+
checks.database = {
|
|
34
|
+
status: 'healthy',
|
|
35
|
+
responseTime: Date.now()
|
|
36
|
+
};
|
|
37
|
+
} catch (error) {
|
|
38
|
+
checks.database = {
|
|
39
|
+
status: 'unhealthy',
|
|
40
|
+
error: String(error)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Check 2: Products API
|
|
45
|
+
try {
|
|
46
|
+
const startTime = Date.now();
|
|
47
|
+
await Products.get(1, 1);
|
|
48
|
+
const responseTime = Date.now() - startTime;
|
|
49
|
+
|
|
50
|
+
checks.productsApi = {
|
|
51
|
+
status: 'healthy',
|
|
52
|
+
responseTime: `${responseTime}ms`
|
|
53
|
+
};
|
|
54
|
+
} catch (error) {
|
|
55
|
+
checks.productsApi = {
|
|
56
|
+
status: 'unhealthy',
|
|
57
|
+
error: String(error)
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Determine overall health
|
|
62
|
+
const allHealthy = Object.values(checks).every((check: any) => check.status === 'healthy');
|
|
63
|
+
const overallStatus = allHealthy ? 'healthy' : 'degraded';
|
|
64
|
+
|
|
65
|
+
// Log health check result
|
|
66
|
+
await Data.create('health-checks', {
|
|
67
|
+
status: overallStatus,
|
|
68
|
+
checks,
|
|
69
|
+
timestamp: new Date().toISOString()
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Alert if unhealthy (in production, send to monitoring service)
|
|
73
|
+
if (!allHealthy) {
|
|
74
|
+
console.warn('⚠️ System health degraded:', checks);
|
|
75
|
+
// TODO: Send alert to monitoring service
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
status: overallStatus,
|
|
80
|
+
checks,
|
|
81
|
+
timestamp: new Date().toISOString()
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
export default healthCheckJob;
|
|
87
|
+
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { LuaTool } from "lua-cli/skill";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { AI, JobInstance, Jobs } from "../../../dist/api-exports";
|
|
4
|
+
import ApiService from "../services/ApiService";
|
|
5
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
6
|
+
|
|
7
|
+
export default class CreateInlineJobTool implements LuaTool {
|
|
8
|
+
name = "create_inline_job";
|
|
9
|
+
description = "Create a new inline job";
|
|
10
|
+
inputSchema = z.object({});
|
|
11
|
+
|
|
12
|
+
async execute(input: z.infer<typeof this.inputSchema>) {
|
|
13
|
+
const jobId = uuidv4();
|
|
14
|
+
const job = await Jobs.create({
|
|
15
|
+
name: `inline-job-${jobId}`,
|
|
16
|
+
description: "Inline job",
|
|
17
|
+
schedule: {
|
|
18
|
+
type: "once",
|
|
19
|
+
executeAt: new Date(Date.now() + 1000)
|
|
20
|
+
},
|
|
21
|
+
metadata: {
|
|
22
|
+
test: "test"
|
|
23
|
+
},
|
|
24
|
+
execute: async (job: JobInstance) => {
|
|
25
|
+
// console.log("Executing inline job", job);
|
|
26
|
+
console.log("Inline job metadata", job.metadata);
|
|
27
|
+
console.log("Inline job user", job.user());
|
|
28
|
+
console.log("Inline job data", job.data);
|
|
29
|
+
const response = await AI.generate("You are a poet. Write a poem about the following topic:", [{ type: "text", text: "A sheep in the field" }]);
|
|
30
|
+
console.log("AI response", response);
|
|
31
|
+
const apiService = new ApiService();
|
|
32
|
+
const data = await apiService.fetchUserData("123");
|
|
33
|
+
await job.updateMetadata({ test: "test2" });
|
|
34
|
+
const disabled = await job.delete();
|
|
35
|
+
console.log("Inline job deleted", disabled);
|
|
36
|
+
return { success: true, data: response, user: data };
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
return { success: true, job: job };
|
|
41
|
+
}
|
|
42
|
+
}
|