lua-cli 3.0.0-alpha.1 โ†’ 3.0.0-alpha.5

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 (61) hide show
  1. package/dist/api/job.api.service.d.ts +16 -7
  2. package/dist/api/job.api.service.js +21 -5
  3. package/dist/api/postprocessor.api.service.d.ts +61 -1
  4. package/dist/api/postprocessor.api.service.js +35 -0
  5. package/dist/api/preprocessor.api.service.d.ts +61 -1
  6. package/dist/api/preprocessor.api.service.js +35 -0
  7. package/dist/api-exports.d.ts +26 -6
  8. package/dist/api-exports.js +42 -29
  9. package/dist/cli/command-definitions.js +13 -6
  10. package/dist/commands/chat.js +32 -5
  11. package/dist/commands/compile.js +16 -2
  12. package/dist/commands/dev.js +23 -2
  13. package/dist/commands/push.d.ts +6 -2
  14. package/dist/commands/push.js +412 -6
  15. package/dist/commands/test.js +18 -2
  16. package/dist/common/job.instance.d.ts +3 -0
  17. package/dist/common/job.instance.js +8 -0
  18. package/dist/config/constants.d.ts +6 -5
  19. package/dist/config/constants.js +12 -10
  20. package/dist/interfaces/chat.d.ts +30 -1
  21. package/dist/interfaces/jobs.d.ts +21 -0
  22. package/dist/types/skill.d.ts +75 -56
  23. package/dist/types/skill.js +53 -59
  24. package/dist/utils/bundling.d.ts +13 -4
  25. package/dist/utils/bundling.js +83 -26
  26. package/dist/utils/compile.js +27 -6
  27. package/dist/utils/dev-api.d.ts +42 -2
  28. package/dist/utils/dev-api.js +177 -4
  29. package/dist/utils/dev-server.d.ts +1 -1
  30. package/dist/utils/dev-server.js +4 -4
  31. package/dist/utils/dynamic-job-bundler.d.ts +17 -0
  32. package/dist/utils/dynamic-job-bundler.js +143 -0
  33. package/dist/utils/pre-bundle-jobs.d.ts +26 -0
  34. package/dist/utils/pre-bundle-jobs.js +176 -0
  35. package/dist/utils/sandbox-storage.d.ts +48 -0
  36. package/dist/utils/sandbox-storage.js +114 -0
  37. package/dist/utils/sandbox.d.ts +2 -2
  38. package/dist/utils/sandbox.js +23 -7
  39. package/package.json +1 -1
  40. package/template/lua.skill.yaml +47 -0
  41. package/template/package-lock.json +10505 -0
  42. package/template/package.json +2 -1
  43. package/template/src/index.ts +65 -3
  44. package/template/src/tools/CreateInlineJob.ts +42 -0
  45. package/API_REFERENCE.md +0 -1408
  46. package/CHANGELOG.md +0 -236
  47. package/CLI_REFERENCE.md +0 -908
  48. package/GETTING_STARTED.md +0 -1040
  49. package/INSTANCE_TYPES.md +0 -1158
  50. package/README.md +0 -865
  51. package/TEMPLATE_GUIDE.md +0 -1398
  52. package/USER_DATA_INSTANCE.md +0 -621
  53. package/template/AGENT_CONFIGURATION.md +0 -251
  54. package/template/COMPLEX_JOB_EXAMPLES.md +0 -795
  55. package/template/DYNAMIC_JOB_CREATION.md +0 -371
  56. package/template/TOOL_EXAMPLES.md +0 -655
  57. package/template/WEBHOOKS_JOBS_QUICKSTART.md +0 -318
  58. package/template/WEBHOOK_JOB_EXAMPLES.md +0 -817
  59. package/template/src/index-agent-example.ts +0 -201
  60. package/template/src/postprocessors/ResponseFormatter.ts +0 -151
  61. package/template/src/preprocessors/MessageFilter.ts +0 -91
@@ -1,795 +0,0 @@
1
- # Complex Job Example: Game Score Tracker
2
-
3
- **Level**: Advanced
4
- **Demonstrates**: Interval jobs, self-termination, dynamic job creation, real-time monitoring
5
-
6
- ---
7
-
8
- ## ๐ŸŽฏ What It Does
9
-
10
- A complete system for tracking live game scores that:
11
- 1. โœ… Creates a game tracking record
12
- 2. โœ… Creates an interval job that starts at game time
13
- 3. โœ… Fetches score updates every X seconds
14
- 4. โœ… Checks if game is over
15
- 5. โœ… **Stops itself** when game ends
16
- 6. โœ… Logs final results
17
-
18
- **Perfect for**: Sports scores, esports matches, auctions, stock monitoring, server status
19
-
20
- ---
21
-
22
- ## ๐Ÿ“ Files
23
-
24
- **Location**: `src/tools/GameScoreTrackerTool.ts`
25
-
26
- **Contains 3 tools:**
27
- 1. `TrackGameScoresTool` - Start tracking a game
28
- 2. `GetGameScoresTool` - Get current scores
29
- 3. `StopGameTrackingTool` - Manually stop tracking
30
-
31
- ---
32
-
33
- ## ๐ŸŽฎ The Flow
34
-
35
- ### User Perspective
36
-
37
- ```
38
- User: "Track the Lakers vs Warriors game starting at 7 PM"
39
- โ†“
40
- Agent uses: track_game_scores
41
- โ†“
42
- Tool creates:
43
- - Game record in database
44
- - Interval job (updates every 60 seconds)
45
- โ†“
46
- Job starts at 7 PM
47
- โ†“
48
- Every 60 seconds, job:
49
- 1. Fetches latest scores
50
- 2. Updates database
51
- 3. Checks if game finished
52
- โ†“
53
- When game ends:
54
- - Job logs final score
55
- - Job deactivates itself โญ
56
- - Tracking stops automatically
57
- ```
58
-
59
- ---
60
-
61
- ## ๐Ÿ’ป Code Walkthrough
62
-
63
- ### Part 1: Create Game Record
64
-
65
- ```typescript
66
- async execute(input: {
67
- gameId: string;
68
- gameName: string;
69
- startTime: string;
70
- updateIntervalSeconds: number;
71
- }) {
72
- // Create game record
73
- const gameRecord = {
74
- gameId: input.gameId,
75
- gameName: input.gameName,
76
- startTime: input.startTime,
77
- status: 'scheduled',
78
- currentScore: { home: 0, away: 0 },
79
- createdAt: new Date().toISOString()
80
- };
81
-
82
- const game = await Data.create('games', gameRecord);
83
- console.log(`โœ… Game record created: ${game.id}`);
84
- ```
85
-
86
- ### Part 2: Calculate Start Time
87
-
88
- ```typescript
89
- // Calculate when job should start
90
- const gameStartTime = new Date(input.startTime);
91
- const now = new Date();
92
-
93
- // If game hasn't started, wait until start time
94
- // If already started, begin immediately
95
- const jobStartTime = gameStartTime > now ? gameStartTime : now;
96
-
97
- console.log(`โฐ Job will start at: ${jobStartTime.toLocaleString()}`);
98
- ```
99
-
100
- **Key insight**: Job can be created now but scheduled to start later!
101
-
102
- ### Part 3: Capture Variables for Closure
103
-
104
- ```typescript
105
- // Capture for job closure
106
- const gameId = input.gameId;
107
- const gameName = input.gameName;
108
- const intervalSeconds = input.updateIntervalSeconds;
109
- const apiEndpoint = input.apiEndpoint || 'https://api.example.com/scores';
110
- const gameRecordId = game.id;
111
- ```
112
-
113
- ### Part 4: Create Self-Terminating Interval Job
114
-
115
- ```typescript
116
- const job = await Jobs.create({
117
- name: `track-game-${gameId}`,
118
- description: `Live score tracking for ${gameName}`,
119
-
120
- // Interval job - runs every X seconds
121
- schedule: {
122
- type: 'interval',
123
- seconds: intervalSeconds
124
- },
125
-
126
- timeout: 30,
127
- retry: { maxAttempts: 2, backoffSeconds: 10 },
128
-
129
- execute: async () => {
130
- console.log(`๐ŸŽฎ Fetching score update for ${gameId}...`);
131
-
132
- // 1. Get current game data
133
- const gameData = await Data.getEntry('games', gameRecordId);
134
-
135
- // 2. Check if already finished
136
- if (gameData.data.status === 'finished') {
137
- console.log(`๐Ÿ Game ${gameId} finished. Stopping job.`);
138
-
139
- // โญ KEY FEATURE: Job stops itself!
140
- await Jobs.deactivate(job.jobId);
141
-
142
- return {
143
- action: 'job-stopped',
144
- reason: 'game-finished',
145
- finalScore: gameData.data.currentScore
146
- };
147
- }
148
-
149
- // 3. Fetch latest scores from API
150
- const response = await fetch(`${apiEndpoint}/${gameId}`);
151
- const scoreUpdate = await response.json();
152
-
153
- // 4. Update database
154
- await Data.update('games', gameRecordId, {
155
- ...gameData.data,
156
- currentScore: {
157
- home: scoreUpdate.home,
158
- away: scoreUpdate.away
159
- },
160
- quarter: scoreUpdate.quarter,
161
- status: scoreUpdate.isFinished ? 'finished' : 'in-progress',
162
- lastUpdate: new Date().toISOString()
163
- });
164
-
165
- console.log(`๐Ÿ“Š Score: ${scoreUpdate.home} - ${scoreUpdate.away}`);
166
-
167
- // 5. If game just ended, stop the job
168
- if (scoreUpdate.isFinished) {
169
- console.log(`๐Ÿ Game ${gameId} ended!`);
170
-
171
- // Store final result
172
- await Data.create('game-results', {
173
- gameId: gameId,
174
- finalScore: scoreUpdate,
175
- finishedAt: new Date().toISOString()
176
- });
177
-
178
- // โญ Stop the job!
179
- await Jobs.deactivate(job.jobId);
180
-
181
- return {
182
- action: 'game-ended-job-stopped',
183
- finalScore: scoreUpdate
184
- };
185
- }
186
-
187
- // Game still in progress
188
- return {
189
- action: 'score-updated',
190
- currentScore: scoreUpdate
191
- };
192
- }
193
- });
194
-
195
- return {
196
- success: true,
197
- game,
198
- scoreTracker: {
199
- jobId: job.jobId,
200
- updateInterval: intervalSeconds
201
- }
202
- };
203
- }
204
- ```
205
-
206
- ---
207
-
208
- ## ๐Ÿ”‘ Key Concepts
209
-
210
- ### 1. **Self-Terminating Job**
211
-
212
- The job can deactivate itself:
213
-
214
- ```typescript
215
- execute: async () => {
216
- // Check condition
217
- if (gameIsOver) {
218
- // Stop myself!
219
- await Jobs.deactivate(job.jobId);
220
- return { action: 'stopped' };
221
- }
222
-
223
- // Continue running
224
- return { action: 'continue' };
225
- }
226
- ```
227
-
228
- **How it works:**
229
- - Job has access to `Jobs` API in sandbox
230
- - Job can call `Jobs.deactivate(job.jobId)`
231
- - Closure captures `job.jobId` from parent scope
232
- - Job stops scheduling future executions
233
-
234
- ### 2. **Interval Jobs with Start Time**
235
-
236
- ```typescript
237
- // Job created now
238
- const job = await Jobs.create({
239
- schedule: {
240
- type: 'interval',
241
- seconds: 60 // Every 60 seconds
242
- }
243
- });
244
-
245
- // But starts at specific time (gameStartTime)
246
- // Backend scheduler waits until start time, then begins interval
247
- ```
248
-
249
- ### 3. **Closure Capture**
250
-
251
- ```typescript
252
- const gameId = 'game_123';
253
- const apiEndpoint = 'https://api.example.com';
254
-
255
- execute: async () => {
256
- // These variables are captured!
257
- const response = await fetch(`${apiEndpoint}/${gameId}`);
258
- // โ†‘ โ†‘
259
- // Both available!
260
- }
261
- ```
262
-
263
- ### 4. **Error Handling Without Stopping**
264
-
265
- ```typescript
266
- execute: async () => {
267
- try {
268
- // Try to fetch from external API
269
- const response = await fetch(apiEndpoint);
270
- const data = await response.json();
271
- } catch (error) {
272
- console.error('API failed:', error);
273
-
274
- // Don't stop job - just log error and continue
275
- return { action: 'error', error: error.message };
276
- }
277
- }
278
- ```
279
-
280
- Job continues running on next interval even if this execution failed.
281
-
282
- ---
283
-
284
- ## ๐Ÿงช Testing the Example
285
-
286
- ### Step 1: Enable in Template
287
-
288
- ```typescript
289
- // In src/index.ts, add:
290
- import {
291
- TrackGameScoresTool,
292
- GetGameScoresTool,
293
- StopGameTrackingTool
294
- } from "./tools/GameScoreTrackerTool";
295
-
296
- const gamesSkill = new LuaSkill({
297
- name: "games-skill",
298
- version: "1.0.0",
299
- description: "Live game score tracking",
300
- context: "Track live sports scores with automatic updates",
301
- tools: [
302
- new TrackGameScoresTool(),
303
- new GetGameScoresTool(),
304
- new StopGameTrackingTool()
305
- ]
306
- });
307
- ```
308
-
309
- ### Step 2: Compile & Test
310
-
311
- ```bash
312
- lua compile
313
- lua test skill
314
-
315
- # Select: track_game_scores
316
- # Enter:
317
- # - gameId: LAL-GSW-2025-10-12
318
- # - gameName: Lakers vs Warriors
319
- # - startTime: 2025-10-12T19:00:00Z
320
- # - updateIntervalSeconds: 60
321
-
322
- # Output:
323
- # โœ… Game record created
324
- # โœ… Score tracking job created: job_abc123
325
- # ๐Ÿ“Š Job will update scores every 60 seconds
326
- # โน๏ธ Job will automatically stop when game ends
327
- ```
328
-
329
- ### Step 3: Monitor Job
330
-
331
- ```bash
332
- # Check job status
333
- lua jobs production
334
- # Select: View deployed jobs
335
- # You'll see: track-game-LAL-GSW-2025-10-12
336
-
337
- # Trigger manually to test
338
- lua jobs production
339
- # Select: Trigger job now
340
- # See: Score update happens immediately
341
-
342
- # View execution history
343
- lua jobs production
344
- # Select: View execution history
345
- # See: All score updates logged
346
- ```
347
-
348
- ### Step 4: Get Current Scores
349
-
350
- ```bash
351
- lua test skill
352
-
353
- # Select: get_game_scores
354
- # Enter: gameId: LAL-GSW-2025-10-12
355
-
356
- # Output:
357
- # {
358
- # "game": {
359
- # "name": "Lakers vs Warriors",
360
- # "status": "in-progress",
361
- # "currentScore": { "home": 45, "away": 42 },
362
- # "quarter": 2,
363
- # "timeRemaining": "5:23"
364
- # }
365
- # }
366
- ```
367
-
368
- ### Step 5: Verify Auto-Stop
369
-
370
- When the game ends:
371
- ```
372
- Job executes
373
- โ†“
374
- Fetches score: { isFinished: true }
375
- โ†“
376
- Logs final score
377
- โ†“
378
- Calls Jobs.deactivate(job.jobId)
379
- โ†“
380
- Job stops - no more executions! โœ…
381
- ```
382
-
383
- ---
384
-
385
- ## ๐ŸŽ“ What This Example Teaches
386
-
387
- ### Advanced Patterns
388
-
389
- 1. **Self-terminating jobs** - Jobs can stop themselves
390
- 2. **Interval with start time** - Delay first execution
391
- 3. **External API integration** - Fetch from external services
392
- 4. **Error recovery** - Continue on errors, stop on success
393
- 5. **Closure serialization** - Complex variable capture
394
- 6. **Mock data fallback** - Demo mode when API unavailable
395
-
396
- ### Real-World Applications
397
-
398
- **Sports Scores:**
399
- - Track NBA, NFL, soccer games
400
- - Update scores every minute
401
- - Stop when game ends
402
-
403
- **Auctions:**
404
- - Monitor auction bids
405
- - Update every 30 seconds
406
- - Stop when auction closes
407
-
408
- **Stock Monitoring:**
409
- - Track stock prices
410
- - Update every 5 minutes during market hours
411
- - Stop when market closes
412
-
413
- **Server Monitoring:**
414
- - Check server health every 1 minute
415
- - Stop when maintenance window ends
416
-
417
- **Trial Periods:**
418
- - Check trial status daily
419
- - Stop when user converts or trial ends
420
-
421
- ---
422
-
423
- ## ๐Ÿ”ง Customization Examples
424
-
425
- ### Change Update Frequency
426
-
427
- ```typescript
428
- // More frequent updates (every 30 seconds)
429
- updateIntervalSeconds: 30
430
-
431
- // Less frequent (every 5 minutes)
432
- updateIntervalSeconds: 300
433
- ```
434
-
435
- ### Add Notifications
436
-
437
- ```typescript
438
- execute: async () => {
439
- const scoreUpdate = await fetchScores();
440
-
441
- // Check for significant events
442
- if (scoreUpdate.home - scoreUpdate.away >= 10) {
443
- // Big lead! Send notification
444
- await Data.create('notifications', {
445
- type: 'big-lead',
446
- gameId: gameId,
447
- score: scoreUpdate,
448
- timestamp: new Date().toISOString()
449
- });
450
- }
451
-
452
- return { scoreUpdate };
453
- }
454
- ```
455
-
456
- ### Multiple Stop Conditions
457
-
458
- ```typescript
459
- execute: async () => {
460
- const gameData = await Data.getEntry('games', gameRecordId);
461
-
462
- // Stop if game finished
463
- if (gameData.data.status === 'finished') {
464
- await Jobs.deactivate(job.jobId);
465
- return { action: 'stopped', reason: 'game-finished' };
466
- }
467
-
468
- // Stop if timeout (3 hours)
469
- const startTime = new Date(gameData.data.startTime);
470
- const elapsed = Date.now() - startTime.getTime();
471
- const threeHours = 3 * 60 * 60 * 1000;
472
-
473
- if (elapsed > threeHours) {
474
- await Jobs.deactivate(job.jobId);
475
- return { action: 'stopped', reason: 'timeout' };
476
- }
477
-
478
- // Continue monitoring
479
- return { action: 'continue' };
480
- }
481
- ```
482
-
483
- ---
484
-
485
- ## ๐Ÿ“Š Job Lifecycle
486
-
487
- ```
488
- t=0: Tool creates job
489
- |
490
- | Schedule: { type: 'interval', seconds: 60 }
491
- | Start time: 7:00 PM
492
- โ†“
493
- t=7:00 PM: First execution
494
- |
495
- โ†“
496
- t=7:01 PM: Second execution (60s later)
497
- |
498
- โ†“
499
- t=7:02 PM: Third execution
500
- |
501
- | ... continues every 60 seconds ...
502
- โ†“
503
- t=9:30 PM: Game ends detected
504
- |
505
- | Calls: await Jobs.deactivate(job.jobId)
506
- โ†“
507
- t=9:30 PM: Job stops โน๏ธ
508
- |
509
- | No more executions scheduled
510
- โ†“
511
- Done!
512
- ```
513
-
514
- ---
515
-
516
- ## ๐ŸŽฏ Use Cases Beyond Games
517
-
518
- ### 1. Auction Monitoring
519
-
520
- ```typescript
521
- await Jobs.create({
522
- name: `monitor-auction-${auctionId}`,
523
- schedule: {
524
- type: 'interval',
525
- seconds: 30
526
- },
527
- execute: async () => {
528
- const auction = await Data.getEntry('auctions', auctionId);
529
-
530
- // Check if auction closed
531
- if (new Date(auction.data.endTime) < new Date()) {
532
- console.log('Auction closed!');
533
- await Jobs.deactivate(job.jobId);
534
- return { action: 'stopped', winner: auction.data.highestBidder };
535
- }
536
-
537
- // Update current bid
538
- const currentBid = await fetchLatestBid(auctionId);
539
- await Data.update('auctions', auctionId, { currentBid });
540
-
541
- return { action: 'updated', currentBid };
542
- }
543
- });
544
- ```
545
-
546
- ### 2. Server Health Monitoring (with time limit)
547
-
548
- ```typescript
549
- await Jobs.create({
550
- name: `health-check-deployment-${deploymentId}`,
551
- schedule: {
552
- type: 'interval',
553
- seconds: 10 // Check every 10 seconds
554
- },
555
- execute: async () => {
556
- const deployment = await Data.getEntry('deployments', deploymentId);
557
-
558
- // Stop if deployment succeeded
559
- if (deployment.data.status === 'success') {
560
- await Jobs.deactivate(job.jobId);
561
- return { action: 'stopped', reason: 'deployment-successful' };
562
- }
563
-
564
- // Stop if timeout (10 minutes)
565
- const elapsed = Date.now() - new Date(deployment.data.startTime).getTime();
566
- if (elapsed > 10 * 60 * 1000) {
567
- await Jobs.deactivate(job.jobId);
568
- return { action: 'stopped', reason: 'timeout' };
569
- }
570
-
571
- // Check health
572
- const health = await checkServerHealth();
573
- await Data.update('deployments', deploymentId, { health });
574
-
575
- return { action: 'monitoring', health };
576
- }
577
- });
578
- ```
579
-
580
- ### 3. Limited-Time Promotion
581
-
582
- ```typescript
583
- const promotionEnd = new Date('2025-12-25T23:59:59Z');
584
-
585
- await Jobs.create({
586
- name: `check-promotion-${promoId}`,
587
- schedule: {
588
- type: 'interval',
589
- seconds: 3600 // Check every hour
590
- },
591
- execute: async () => {
592
- // Check if promotion ended
593
- if (new Date() > promotionEnd) {
594
- console.log('Promotion ended');
595
-
596
- // Deactivate promotion
597
- await Data.update('promotions', promoId, { active: false });
598
-
599
- // Stop this job
600
- await Jobs.deactivate(job.jobId);
601
-
602
- return { action: 'stopped', reason: 'promotion-ended' };
603
- }
604
-
605
- // Log promotion stats
606
- const stats = await getPromotionStats(promoId);
607
- await Data.create('promotion-stats', {
608
- promoId,
609
- stats,
610
- timestamp: new Date().toISOString()
611
- });
612
-
613
- return { action: 'logged', stats };
614
- }
615
- });
616
- ```
617
-
618
- ---
619
-
620
- ## ๐Ÿ’ก Advanced Techniques
621
-
622
- ### Conditional Stop Logic
623
-
624
- ```typescript
625
- execute: async () => {
626
- const data = await Data.getEntry('collection', recordId);
627
-
628
- // Multiple stop conditions
629
- const stopReasons = [];
630
-
631
- if (data.data.completed) stopReasons.push('completed');
632
- if (data.data.cancelled) stopReasons.push('cancelled');
633
- if (data.data.timeout) stopReasons.push('timeout');
634
-
635
- if (stopReasons.length > 0) {
636
- await Jobs.deactivate(job.jobId);
637
- return {
638
- action: 'stopped',
639
- reasons: stopReasons
640
- };
641
- }
642
-
643
- // Continue
644
- return { action: 'continue' };
645
- }
646
- ```
647
-
648
- ### Gradual Slowdown
649
-
650
- ```typescript
651
- let executionCount = 0;
652
-
653
- execute: async () => {
654
- executionCount++;
655
-
656
- // After 100 executions, slow down the interval
657
- if (executionCount >= 100) {
658
- // Create a new slower job
659
- await Jobs.create({
660
- name: `${originalName}-slow`,
661
- schedule: { type: 'interval', seconds: 300 } // 5 min instead of 1 min
662
- });
663
-
664
- // Stop fast job
665
- await Jobs.deactivate(job.jobId);
666
-
667
- return { action: 'switched-to-slow-mode' };
668
- }
669
-
670
- return { action: 'continue', count: executionCount };
671
- }
672
- ```
673
-
674
- ### Pause and Resume Based on Time
675
-
676
- ```typescript
677
- execute: async () => {
678
- const hour = new Date().getHours();
679
-
680
- // Only run during business hours (9 AM - 5 PM)
681
- if (hour < 9 || hour >= 17) {
682
- console.log('Outside business hours, skipping');
683
- return { action: 'skipped', hour };
684
- }
685
-
686
- // Do work
687
- const result = await doWork();
688
-
689
- return { action: 'processed', result };
690
- }
691
- ```
692
-
693
- ---
694
-
695
- ## ๐Ÿงช Testing Scenarios
696
-
697
- ### Test 1: Game In Progress
698
-
699
- ```bash
700
- lua test skill
701
- # Select: track_game_scores
702
- # Enter:
703
- # gameId: TEST-001
704
- # gameName: Test Game
705
- # startTime: 2025-10-12T20:00:00Z
706
- # updateIntervalSeconds: 60
707
-
708
- # Result: Job created and will run every 60 seconds
709
- ```
710
-
711
- ### Test 2: Get Scores
712
-
713
- ```bash
714
- lua test skill
715
- # Select: get_game_scores
716
- # Enter: gameId: TEST-001
717
-
718
- # Result: Current score and game status
719
- ```
720
-
721
- ### Test 3: Manual Stop
722
-
723
- ```bash
724
- lua test skill
725
- # Select: stop_game_tracking
726
- # Enter: gameId: TEST-001
727
-
728
- # Result: Job deactivated, game marked as stopped
729
- ```
730
-
731
- ### Test 4: Trigger Job Manually
732
-
733
- ```bash
734
- lua jobs production
735
- # Select: Trigger job now
736
- # Select: track-game-TEST-001
737
-
738
- # Job executes immediately and logs result
739
- ```
740
-
741
- ---
742
-
743
- ## ๐Ÿ“‹ Checklist for Your Own Implementation
744
-
745
- When creating similar self-terminating interval jobs:
746
-
747
- - [ ] Capture all needed variables before job creation
748
- - [ ] Use interval schedule with appropriate frequency
749
- - [ ] Include stop condition check at start of execute
750
- - [ ] Call `Jobs.deactivate(job.jobId)` when stopping
751
- - [ ] Log why the job stopped
752
- - [ ] Store final results before stopping
753
- - [ ] Handle API errors gracefully (don't stop on errors)
754
- - [ ] Set appropriate timeout
755
- - [ ] Configure retry for transient errors
756
- - [ ] Test manual triggering
757
-
758
- ---
759
-
760
- ## ๐ŸŽ“ Learn More
761
-
762
- ### Similar Examples
763
- - **Stock Price Tracker** - Monitor stocks during market hours, stop at close
764
- - **Build Monitor** - Check CI/CD status every 10s, stop when complete
765
- - **Trial Countdown** - Check daily, stop when trial converts/expires
766
- - **Flash Sale** - Update inventory every 5 seconds, stop when sold out
767
-
768
- ### Documentation
769
- - **DYNAMIC_JOB_CREATION_API.md** - Jobs.create() reference
770
- - **WEBHOOK_JOB_EXAMPLES.md** - More examples
771
- - **JOB_API_SPEC.md** - Complete API reference
772
-
773
- ---
774
-
775
- ## โœ… Summary
776
-
777
- **This example demonstrates:**
778
- - โœ… Creating interval jobs dynamically
779
- - โœ… Jobs that stop themselves
780
- - โœ… Real-time monitoring patterns
781
- - โœ… External API integration
782
- - โœ… Error handling in jobs
783
- - โœ… Closure capture
784
- - โœ… Normal TypeScript code (not strings!)
785
-
786
- **Perfect for:**
787
- - Live data monitoring
788
- - Time-bounded tracking
789
- - Event-driven scheduling
790
- - Self-managing background tasks
791
-
792
- ---
793
-
794
- **Now you can build sophisticated real-time monitoring systems with clean, self-managing code!** ๐ŸŽฎโœจ
795
-