lua-cli 2.5.8 → 3.0.0-alpha.4

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 (115) hide show
  1. package/dist/api/job.api.service.d.ts +219 -0
  2. package/dist/api/job.api.service.js +216 -0
  3. package/dist/api/lazy-instances.d.ts +24 -0
  4. package/dist/api/lazy-instances.js +48 -0
  5. package/dist/api/postprocessor.api.service.d.ts +158 -0
  6. package/dist/api/postprocessor.api.service.js +111 -0
  7. package/dist/api/preprocessor.api.service.d.ts +158 -0
  8. package/dist/api/preprocessor.api.service.js +111 -0
  9. package/dist/api/user.data.api.service.d.ts +13 -0
  10. package/dist/api/user.data.api.service.js +20 -0
  11. package/dist/api/webhook.api.service.d.ts +151 -0
  12. package/dist/api/webhook.api.service.js +134 -0
  13. package/dist/api-exports.d.ts +176 -41
  14. package/dist/api-exports.js +195 -21
  15. package/dist/cli/command-definitions.js +75 -5
  16. package/dist/commands/chat.js +32 -5
  17. package/dist/commands/compile.js +140 -7
  18. package/dist/commands/dev.js +23 -2
  19. package/dist/commands/index.d.ts +4 -0
  20. package/dist/commands/index.js +4 -0
  21. package/dist/commands/init.js +53 -7
  22. package/dist/commands/jobs.d.ts +20 -0
  23. package/dist/commands/jobs.js +533 -0
  24. package/dist/commands/logs.js +2 -5
  25. package/dist/commands/postprocessors.d.ts +8 -0
  26. package/dist/commands/postprocessors.js +431 -0
  27. package/dist/commands/preprocessors.d.ts +8 -0
  28. package/dist/commands/preprocessors.js +431 -0
  29. package/dist/commands/push.js +686 -5
  30. package/dist/commands/test.d.ts +9 -18
  31. package/dist/commands/test.js +574 -82
  32. package/dist/commands/webhooks.d.ts +18 -0
  33. package/dist/commands/webhooks.js +424 -0
  34. package/dist/common/job.instance.d.ts +80 -0
  35. package/dist/common/job.instance.js +116 -0
  36. package/dist/common/user.instance.d.ts +1 -0
  37. package/dist/common/user.instance.js +9 -0
  38. package/dist/config/constants.d.ts +4 -3
  39. package/dist/config/constants.js +10 -8
  40. package/dist/interfaces/agent.d.ts +2 -1
  41. package/dist/interfaces/chat.d.ts +52 -1
  42. package/dist/interfaces/index.d.ts +10 -0
  43. package/dist/interfaces/index.js +7 -0
  44. package/dist/interfaces/jobs.d.ts +193 -0
  45. package/dist/interfaces/jobs.js +5 -0
  46. package/dist/interfaces/postprocessors.d.ts +35 -0
  47. package/dist/interfaces/postprocessors.js +4 -0
  48. package/dist/interfaces/preprocessors.d.ts +35 -0
  49. package/dist/interfaces/preprocessors.js +4 -0
  50. package/dist/interfaces/webhooks.d.ts +104 -0
  51. package/dist/interfaces/webhooks.js +5 -0
  52. package/dist/types/api-contracts.d.ts +5 -0
  53. package/dist/types/compile.types.d.ts +49 -0
  54. package/dist/types/index.d.ts +1 -1
  55. package/dist/types/index.js +1 -1
  56. package/dist/types/skill.d.ts +521 -0
  57. package/dist/types/skill.js +471 -0
  58. package/dist/utils/agent-management.d.ts +25 -0
  59. package/dist/utils/agent-management.js +67 -0
  60. package/dist/utils/bundling.d.ts +44 -5
  61. package/dist/utils/bundling.js +723 -23
  62. package/dist/utils/compile.d.ts +63 -0
  63. package/dist/utils/compile.js +712 -36
  64. package/dist/utils/deployment.d.ts +2 -1
  65. package/dist/utils/deployment.js +16 -2
  66. package/dist/utils/dev-api.d.ts +42 -2
  67. package/dist/utils/dev-api.js +177 -4
  68. package/dist/utils/dev-server.d.ts +1 -1
  69. package/dist/utils/dev-server.js +4 -4
  70. package/dist/utils/dynamic-job-bundler.d.ts +17 -0
  71. package/dist/utils/dynamic-job-bundler.js +143 -0
  72. package/dist/utils/init-agent.d.ts +3 -1
  73. package/dist/utils/init-agent.js +6 -4
  74. package/dist/utils/init-prompts.d.ts +2 -1
  75. package/dist/utils/init-prompts.js +14 -9
  76. package/dist/utils/job-management.d.ts +24 -0
  77. package/dist/utils/job-management.js +264 -0
  78. package/dist/utils/postprocessor-management.d.ts +9 -0
  79. package/dist/utils/postprocessor-management.js +118 -0
  80. package/dist/utils/pre-bundle-jobs.d.ts +26 -0
  81. package/dist/utils/pre-bundle-jobs.js +176 -0
  82. package/dist/utils/preprocessor-management.d.ts +9 -0
  83. package/dist/utils/preprocessor-management.js +118 -0
  84. package/dist/utils/sandbox-storage.d.ts +48 -0
  85. package/dist/utils/sandbox-storage.js +114 -0
  86. package/dist/utils/sandbox.d.ts +61 -1
  87. package/dist/utils/sandbox.js +299 -72
  88. package/dist/utils/tool-detection.d.ts +3 -2
  89. package/dist/utils/tool-detection.js +18 -4
  90. package/dist/utils/webhook-management.d.ts +24 -0
  91. package/dist/utils/webhook-management.js +256 -0
  92. package/package.json +1 -1
  93. package/template/README.md +30 -2
  94. package/template/lua.skill.yaml +47 -0
  95. package/template/package-lock.json +10505 -0
  96. package/template/package.json +2 -1
  97. package/template/src/index.ts +103 -2
  98. package/template/src/jobs/AbandonedBasketProcessorJob.ts +139 -0
  99. package/template/src/jobs/DailyCleanupJob.ts +100 -0
  100. package/template/src/jobs/DataMigrationJob.ts +133 -0
  101. package/template/src/jobs/HealthCheckJob.ts +87 -0
  102. package/template/src/tools/CreateInlineJob.ts +42 -0
  103. package/template/src/tools/GameScoreTrackerTool.ts +356 -0
  104. package/template/src/tools/SmartBasketTool.ts +188 -0
  105. package/template/src/webhooks/PaymentWebhook.ts +113 -0
  106. package/template/src/webhooks/UserEventWebhook.ts +77 -0
  107. package/API_REFERENCE.md +0 -1408
  108. package/CHANGELOG.md +0 -236
  109. package/CLI_REFERENCE.md +0 -908
  110. package/GETTING_STARTED.md +0 -1040
  111. package/INSTANCE_TYPES.md +0 -1158
  112. package/README.md +0 -865
  113. package/TEMPLATE_GUIDE.md +0 -1398
  114. package/USER_DATA_INSTANCE.md +0 -621
  115. package/template/TOOL_EXAMPLES.md +0 -655
@@ -115,8 +115,8 @@ export function extractToolFromNewExpressionSync(newExpr, project) {
115
115
  try {
116
116
  const expression = newExpr.getExpression();
117
117
  const className = expression.getText();
118
- // Find the import declaration for this class
119
118
  const sourceFile = newExpr.getSourceFile();
119
+ // Strategy 1: Try to find the tool via imports (separate file)
120
120
  const imports = sourceFile.getImportDeclarations();
121
121
  for (const importDecl of imports) {
122
122
  const namedImports = importDecl.getNamedImports();
@@ -134,43 +134,20 @@ export function extractToolFromNewExpressionSync(newExpr, project) {
134
134
  toolFilePath = resolveImportPath(importDecl.getModuleSpecifierValue(), sourceFile.getFilePath());
135
135
  }
136
136
  if (toolFilePath && fs.existsSync(toolFilePath)) {
137
- // Extract tool metadata from the class file
138
- try {
139
- const toolSourceFile = project.addSourceFileAtPath(toolFilePath);
140
- const classDecl = toolSourceFile.getClass(className);
141
- if (classDecl) {
142
- const nameProperty = classDecl.getProperty('name');
143
- const descProperty = classDecl.getProperty('description');
144
- let toolName = className.replace(/Tool$/, '').toLowerCase();
145
- let description = '';
146
- // Extract name from property if available
147
- if (nameProperty && nameProperty.getInitializer()) {
148
- const nameValue = nameProperty.getInitializer()?.getText();
149
- if (nameValue) {
150
- toolName = nameValue.replace(/['"]/g, '');
151
- }
152
- }
153
- // Extract description from property if available
154
- if (descProperty && descProperty.getInitializer()) {
155
- const descValue = descProperty.getInitializer()?.getText();
156
- if (descValue) {
157
- description = descValue.replace(/['"]/g, '');
158
- }
159
- }
160
- const toolInfo = {
161
- name: toolName,
162
- className,
163
- filePath: toolFilePath,
164
- description
165
- };
166
- return toolInfo;
167
- }
168
- }
169
- catch (fileError) {
170
- console.warn(`Warning: Could not load tool file ${toolFilePath}:`, fileError);
171
- }
137
+ // Extract tool metadata from the separate file
138
+ const toolInfo = extractToolMetadataFromFile(toolFilePath, className, project);
139
+ if (toolInfo)
140
+ return toolInfo;
172
141
  }
173
142
  }
143
+ // Strategy 2: Check if tool is defined inline in the same file
144
+ const classDecl = sourceFile.getClass(className);
145
+ if (classDecl) {
146
+ // Tool is defined inline - extract metadata directly
147
+ const toolInfo = extractToolMetadataFromClass(classDecl, className, sourceFile.getFilePath());
148
+ if (toolInfo)
149
+ return toolInfo;
150
+ }
174
151
  return null;
175
152
  }
176
153
  catch (error) {
@@ -178,6 +155,51 @@ export function extractToolFromNewExpressionSync(newExpr, project) {
178
155
  return null;
179
156
  }
180
157
  }
158
+ /**
159
+ * Extracts tool metadata from a separate tool file.
160
+ */
161
+ function extractToolMetadataFromFile(toolFilePath, className, project) {
162
+ try {
163
+ const toolSourceFile = project.addSourceFileAtPath(toolFilePath);
164
+ const classDecl = toolSourceFile.getClass(className);
165
+ if (classDecl) {
166
+ return extractToolMetadataFromClass(classDecl, className, toolFilePath);
167
+ }
168
+ }
169
+ catch (fileError) {
170
+ console.warn(`Warning: Could not load tool file ${toolFilePath}:`, fileError);
171
+ }
172
+ return null;
173
+ }
174
+ /**
175
+ * Extracts tool metadata from a class declaration (works for both inline and separate files).
176
+ */
177
+ function extractToolMetadataFromClass(classDecl, className, filePath) {
178
+ const nameProperty = classDecl.getProperty('name');
179
+ const descProperty = classDecl.getProperty('description');
180
+ let toolName = className.replace(/Tool$/, '').toLowerCase();
181
+ let description = '';
182
+ // Extract name from property if available
183
+ if (nameProperty && nameProperty.getInitializer()) {
184
+ const nameValue = nameProperty.getInitializer()?.getText();
185
+ if (nameValue) {
186
+ toolName = nameValue.replace(/['"]/g, '');
187
+ }
188
+ }
189
+ // Extract description from property if available
190
+ if (descProperty && descProperty.getInitializer()) {
191
+ const descValue = descProperty.getInitializer()?.getText();
192
+ if (descValue) {
193
+ description = descValue.replace(/['"]/g, '');
194
+ }
195
+ }
196
+ return {
197
+ name: toolName,
198
+ className,
199
+ filePath,
200
+ description
201
+ };
202
+ }
181
203
  /**
182
204
  * Extracts skills metadata from index file
183
205
  */
@@ -240,3 +262,657 @@ export function extractSkillsMetadata(indexFile) {
240
262
  });
241
263
  return skills;
242
264
  }
265
+ /**
266
+ * Extracts webhooks metadata from index file
267
+ */
268
+ export function extractWebhooksMetadata(indexFile) {
269
+ const webhooks = [];
270
+ // Find all LuaWebhook constructor calls
271
+ indexFile.forEachDescendant((node) => {
272
+ if (Node.isNewExpression(node)) {
273
+ const expression = node.getExpression();
274
+ if (expression.getText() === 'LuaWebhook') {
275
+ const args = node.getArguments();
276
+ if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
277
+ const configObj = args[0];
278
+ let webhookName = '';
279
+ let webhookVersion = '';
280
+ let description = '';
281
+ let context = '';
282
+ // Extract properties
283
+ configObj.getProperties().forEach((prop) => {
284
+ if (Node.isPropertyAssignment(prop)) {
285
+ const name = prop.getName();
286
+ const value = prop.getInitializer();
287
+ if (name === 'name' && value) {
288
+ webhookName = value.getText().replace(/['"]/g, '');
289
+ }
290
+ else if (name === 'version' && value) {
291
+ webhookVersion = value.getText().replace(/['"]/g, '');
292
+ }
293
+ else if (name === 'description' && value) {
294
+ description = value.getText().replace(/['"]/g, '');
295
+ }
296
+ else if (name === 'context' && value) {
297
+ context = value.getText().replace(/['"]/g, '');
298
+ }
299
+ }
300
+ });
301
+ if (webhookName) {
302
+ webhooks.push({
303
+ name: webhookName,
304
+ version: webhookVersion || '1.0.0',
305
+ description,
306
+ context
307
+ });
308
+ }
309
+ }
310
+ }
311
+ }
312
+ });
313
+ return webhooks;
314
+ }
315
+ /**
316
+ * Extracts jobs metadata from index file
317
+ */
318
+ export function extractJobsMetadata(indexFile) {
319
+ const jobs = [];
320
+ // Find all LuaJob constructor calls
321
+ indexFile.forEachDescendant((node) => {
322
+ if (Node.isNewExpression(node)) {
323
+ const expression = node.getExpression();
324
+ if (expression.getText() === 'LuaJob') {
325
+ const args = node.getArguments();
326
+ if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
327
+ const configObj = args[0];
328
+ let jobName = '';
329
+ let jobVersion = '';
330
+ let description = '';
331
+ let context = '';
332
+ let schedule = null;
333
+ let timeout;
334
+ let retry = undefined;
335
+ let metadata = undefined;
336
+ // Extract properties
337
+ configObj.getProperties().forEach((prop) => {
338
+ if (Node.isPropertyAssignment(prop)) {
339
+ const name = prop.getName();
340
+ const value = prop.getInitializer();
341
+ if (name === 'name' && value) {
342
+ jobName = value.getText().replace(/['"]/g, '');
343
+ }
344
+ else if (name === 'version' && value) {
345
+ jobVersion = value.getText().replace(/['"]/g, '');
346
+ }
347
+ else if (name === 'description' && value) {
348
+ description = value.getText().replace(/['"]/g, '');
349
+ }
350
+ else if (name === 'context' && value) {
351
+ context = value.getText().replace(/['"]/g, '');
352
+ }
353
+ else if (name === 'schedule' && value) {
354
+ // Extract schedule configuration
355
+ try {
356
+ const scheduleText = value.getText();
357
+ // Try to evaluate the schedule object
358
+ schedule = eval(`(${scheduleText})`);
359
+ }
360
+ catch (error) {
361
+ console.warn('Warning: Could not parse schedule:', error);
362
+ schedule = { type: 'unknown' };
363
+ }
364
+ }
365
+ else if (name === 'timeout' && value) {
366
+ const timeoutText = value.getText();
367
+ timeout = parseInt(timeoutText, 10);
368
+ }
369
+ else if (name === 'retry' && value) {
370
+ // Extract retry configuration
371
+ try {
372
+ const retryText = value.getText();
373
+ retry = eval(`(${retryText})`);
374
+ }
375
+ catch (error) {
376
+ console.warn('Warning: Could not parse retry config:', error);
377
+ }
378
+ }
379
+ else if (name === 'metadata' && value) {
380
+ // Extract metadata
381
+ try {
382
+ const metadataText = value.getText();
383
+ metadata = eval(`(${metadataText})`);
384
+ }
385
+ catch (error) {
386
+ console.warn('Warning: Could not parse metadata:', error);
387
+ }
388
+ }
389
+ }
390
+ });
391
+ if (jobName) {
392
+ jobs.push({
393
+ name: jobName,
394
+ version: jobVersion || '1.0.0',
395
+ description,
396
+ context,
397
+ schedule,
398
+ timeout,
399
+ retry,
400
+ metadata
401
+ });
402
+ }
403
+ }
404
+ }
405
+ }
406
+ });
407
+ return jobs;
408
+ }
409
+ /**
410
+ * Extracts preprocessors metadata from index file
411
+ */
412
+ export function extractPreProcessorsMetadata(indexFile) {
413
+ const preprocessors = [];
414
+ // Find all PreProcessor constructor calls
415
+ indexFile.forEachDescendant((node) => {
416
+ if (Node.isNewExpression(node)) {
417
+ const expression = node.getExpression();
418
+ if (expression.getText() === 'PreProcessor') {
419
+ const args = node.getArguments();
420
+ if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
421
+ const configObj = args[0];
422
+ let name = '';
423
+ let version = '';
424
+ let description = '';
425
+ let context = '';
426
+ let asyncMode = false;
427
+ configObj.getProperties().forEach((prop) => {
428
+ if (Node.isPropertyAssignment(prop)) {
429
+ const propName = prop.getName();
430
+ const value = prop.getInitializer();
431
+ if (propName === 'name' && value) {
432
+ name = value.getText().replace(/['"]/g, '');
433
+ }
434
+ else if (propName === 'version' && value) {
435
+ version = value.getText().replace(/['"]/g, '');
436
+ }
437
+ else if (propName === 'description' && value) {
438
+ description = value.getText().replace(/['"]/g, '');
439
+ }
440
+ else if (propName === 'context' && value) {
441
+ context = value.getText().replace(/['"]/g, '');
442
+ }
443
+ else if (propName === 'async' && value) {
444
+ // Properly extract boolean value by checking node kind
445
+ const nodeKind = value.getKind();
446
+ asyncMode = nodeKind === 110; // TrueKeyword = 110, FalseKeyword = 95
447
+ }
448
+ }
449
+ });
450
+ if (name) {
451
+ preprocessors.push({
452
+ name,
453
+ version: version || '1.0.0',
454
+ description,
455
+ context,
456
+ async: asyncMode
457
+ });
458
+ }
459
+ }
460
+ }
461
+ }
462
+ });
463
+ return preprocessors;
464
+ }
465
+ /**
466
+ * Extracts postprocessors metadata from index file
467
+ */
468
+ export function extractPostProcessorsMetadata(indexFile) {
469
+ const postprocessors = [];
470
+ // Find all PostProcessor constructor calls
471
+ indexFile.forEachDescendant((node) => {
472
+ if (Node.isNewExpression(node)) {
473
+ const expression = node.getExpression();
474
+ if (expression.getText() === 'PostProcessor') {
475
+ const args = node.getArguments();
476
+ if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
477
+ const configObj = args[0];
478
+ let name = '';
479
+ let version = '';
480
+ let description = '';
481
+ let context = '';
482
+ let asyncMode = false;
483
+ configObj.getProperties().forEach((prop) => {
484
+ if (Node.isPropertyAssignment(prop)) {
485
+ const propName = prop.getName();
486
+ const value = prop.getInitializer();
487
+ if (propName === 'name' && value) {
488
+ name = value.getText().replace(/['"]/g, '');
489
+ }
490
+ else if (propName === 'version' && value) {
491
+ version = value.getText().replace(/['"]/g, '');
492
+ }
493
+ else if (propName === 'description' && value) {
494
+ description = value.getText().replace(/['"]/g, '');
495
+ }
496
+ else if (propName === 'context' && value) {
497
+ context = value.getText().replace(/['"]/g, '');
498
+ }
499
+ else if (propName === 'async' && value) {
500
+ // Properly extract boolean value by checking node kind
501
+ const nodeKind = value.getKind();
502
+ asyncMode = nodeKind === 110; // TrueKeyword = 110, FalseKeyword = 95
503
+ }
504
+ }
505
+ });
506
+ if (name) {
507
+ postprocessors.push({
508
+ name,
509
+ version: version || '1.0.0',
510
+ description,
511
+ context,
512
+ async: asyncMode
513
+ });
514
+ }
515
+ }
516
+ }
517
+ }
518
+ });
519
+ return postprocessors;
520
+ }
521
+ /**
522
+ * Extracts LuaAgent metadata from index file.
523
+ * Returns the unified agent configuration if found, otherwise null.
524
+ * This function looks for either:
525
+ * - A LuaAgent constructor call: new LuaAgent({ ... })
526
+ * - A variable assigned to LuaAgent: const agent = new LuaAgent({ ... })
527
+ */
528
+ export function extractLuaAgentMetadata(indexFile) {
529
+ let agentMetadata = null;
530
+ // Find LuaAgent constructor calls
531
+ indexFile.forEachDescendant((node) => {
532
+ if (Node.isNewExpression(node)) {
533
+ const expression = node.getExpression();
534
+ if (expression.getText() === 'LuaAgent') {
535
+ const args = node.getArguments();
536
+ if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
537
+ const configObj = args[0];
538
+ let name = '';
539
+ let persona = '';
540
+ let welcomeMessage = '';
541
+ const skills = [];
542
+ const webhooks = [];
543
+ const jobs = [];
544
+ const preProcessors = [];
545
+ const postProcessors = [];
546
+ // Extract properties from LuaAgent config
547
+ configObj.getProperties().forEach((prop) => {
548
+ if (Node.isPropertyAssignment(prop)) {
549
+ const propName = prop.getName();
550
+ const value = prop.getInitializer();
551
+ if (propName === 'name' && value) {
552
+ name = value.getText().replace(/['"]/g, '');
553
+ }
554
+ else if (propName === 'persona' && value) {
555
+ // Handle multi-line strings and template literals
556
+ const personaText = value.getText();
557
+ persona = personaText
558
+ .replace(/^['"`]/, '')
559
+ .replace(/['"`]$/, '')
560
+ .replace(/\\n/g, '\n')
561
+ .trim();
562
+ }
563
+ else if (propName === 'welcomeMessage' && value) {
564
+ welcomeMessage = value.getText().replace(/['"]/g, '');
565
+ }
566
+ else if (propName === 'skills' && value && Node.isArrayLiteralExpression(value)) {
567
+ // Extract skill references from the array
568
+ value.getElements().forEach((element) => {
569
+ // Skills can be variable references or inline new LuaSkill()
570
+ if (Node.isIdentifier(element)) {
571
+ skills.push({ ref: element.getText() });
572
+ }
573
+ else if (Node.isNewExpression(element)) {
574
+ // Extract inline skill metadata
575
+ const skillArgs = element.getArguments();
576
+ if (skillArgs.length > 0 && Node.isObjectLiteralExpression(skillArgs[0])) {
577
+ const skillConfig = extractConfigFromObjectLiteral(skillArgs[0]);
578
+ skills.push(skillConfig);
579
+ }
580
+ }
581
+ });
582
+ }
583
+ else if (propName === 'webhooks' && value && Node.isArrayLiteralExpression(value)) {
584
+ value.getElements().forEach((element) => {
585
+ if (Node.isIdentifier(element)) {
586
+ webhooks.push({ ref: element.getText() });
587
+ }
588
+ else if (Node.isNewExpression(element)) {
589
+ const webhookArgs = element.getArguments();
590
+ if (webhookArgs.length > 0 && Node.isObjectLiteralExpression(webhookArgs[0])) {
591
+ const webhookConfig = extractConfigFromObjectLiteral(webhookArgs[0]);
592
+ webhooks.push(webhookConfig);
593
+ }
594
+ }
595
+ });
596
+ }
597
+ else if (propName === 'jobs' && value && Node.isArrayLiteralExpression(value)) {
598
+ value.getElements().forEach((element) => {
599
+ if (Node.isIdentifier(element)) {
600
+ jobs.push({ ref: element.getText() });
601
+ }
602
+ else if (Node.isNewExpression(element)) {
603
+ const jobArgs = element.getArguments();
604
+ if (jobArgs.length > 0 && Node.isObjectLiteralExpression(jobArgs[0])) {
605
+ const jobConfig = extractConfigFromObjectLiteral(jobArgs[0]);
606
+ jobs.push(jobConfig);
607
+ }
608
+ }
609
+ });
610
+ }
611
+ else if (propName === 'preProcessors' && value && Node.isArrayLiteralExpression(value)) {
612
+ value.getElements().forEach((element) => {
613
+ if (Node.isIdentifier(element)) {
614
+ preProcessors.push({ ref: element.getText() });
615
+ }
616
+ else if (Node.isNewExpression(element)) {
617
+ const processorArgs = element.getArguments();
618
+ if (processorArgs.length > 0 && Node.isObjectLiteralExpression(processorArgs[0])) {
619
+ const processorConfig = extractConfigFromObjectLiteral(processorArgs[0]);
620
+ preProcessors.push(processorConfig);
621
+ }
622
+ }
623
+ });
624
+ }
625
+ else if (propName === 'postProcessors' && value && Node.isArrayLiteralExpression(value)) {
626
+ value.getElements().forEach((element) => {
627
+ if (Node.isIdentifier(element)) {
628
+ postProcessors.push({ ref: element.getText() });
629
+ }
630
+ else if (Node.isNewExpression(element)) {
631
+ const processorArgs = element.getArguments();
632
+ if (processorArgs.length > 0 && Node.isObjectLiteralExpression(processorArgs[0])) {
633
+ const processorConfig = extractConfigFromObjectLiteral(processorArgs[0]);
634
+ postProcessors.push(processorConfig);
635
+ }
636
+ }
637
+ });
638
+ }
639
+ }
640
+ });
641
+ if (name && persona) {
642
+ agentMetadata = {
643
+ name,
644
+ persona,
645
+ welcomeMessage: welcomeMessage || undefined,
646
+ skills,
647
+ webhooks,
648
+ jobs,
649
+ preProcessors,
650
+ postProcessors
651
+ };
652
+ }
653
+ }
654
+ }
655
+ }
656
+ });
657
+ return agentMetadata;
658
+ }
659
+ /**
660
+ * Helper function to extract configuration from an object literal expression.
661
+ * Used by extractLuaAgentMetadata to parse inline skill/webhook/job definitions.
662
+ * Handles nested objects, arrays, and various data types.
663
+ */
664
+ function extractConfigFromObjectLiteral(configObj) {
665
+ const config = {};
666
+ configObj.getProperties().forEach((prop) => {
667
+ if (Node.isPropertyAssignment(prop)) {
668
+ const name = prop.getName();
669
+ const value = prop.getInitializer();
670
+ if (value) {
671
+ config[name] = extractValueFromNode(value);
672
+ }
673
+ }
674
+ });
675
+ return config;
676
+ }
677
+ /**
678
+ * Extracts a value from an AST node, handling various types.
679
+ */
680
+ function extractValueFromNode(node) {
681
+ if (!node)
682
+ return undefined;
683
+ // String literal
684
+ if (Node.isStringLiteral(node)) {
685
+ return node.getLiteralValue();
686
+ }
687
+ // Numeric literal
688
+ if (Node.isNumericLiteral(node)) {
689
+ return node.getLiteralValue();
690
+ }
691
+ // Boolean literal (TrueKeyword = 110, FalseKeyword = 95)
692
+ const nodeKind = node.getKind();
693
+ if (nodeKind === 110)
694
+ return true; // TrueKeyword
695
+ if (nodeKind === 95)
696
+ return false; // FalseKeyword
697
+ // New expression (for tools: [new ToolClass()])
698
+ // Extract just the class name for compatibility with buildSkillsArray
699
+ if (Node.isNewExpression(node)) {
700
+ const expression = node.getExpression();
701
+ return expression.getText(); // Returns class name like "GetWeatherTool"
702
+ }
703
+ // Array literal
704
+ if (Node.isArrayLiteralExpression(node)) {
705
+ return node.getElements().map((element) => extractValueFromNode(element));
706
+ }
707
+ // Object literal
708
+ if (Node.isObjectLiteralExpression(node)) {
709
+ return extractConfigFromObjectLiteral(node);
710
+ }
711
+ // Arrow function or function expression (for execute, etc.)
712
+ if (Node.isArrowFunction(node) || Node.isFunctionExpression(node)) {
713
+ return node.getText(); // Return the function as a string
714
+ }
715
+ // Template literal or other complex expressions
716
+ // For persona, context, etc. that might be multiline
717
+ const text = node.getText();
718
+ // Handle boolean strings (fallback if node kind check didn't catch it)
719
+ if (text === 'true')
720
+ return true;
721
+ if (text === 'false')
722
+ return false;
723
+ // If it starts with a quote, remove quotes
724
+ if ((text.startsWith('"') && text.endsWith('"')) ||
725
+ (text.startsWith("'") && text.endsWith("'")) ||
726
+ (text.startsWith('`') && text.endsWith('`'))) {
727
+ return text.slice(1, -1).replace(/\\n/g, '\n');
728
+ }
729
+ return text;
730
+ }
731
+ /**
732
+ * Gets the file paths where skills are defined in a LuaAgent.
733
+ * This is used to scan those files for tools.
734
+ * Returns deduplicated file paths.
735
+ *
736
+ * @param agentMetadata - Agent metadata from extractLuaAgentMetadata
737
+ * @param indexFile - The index source file
738
+ * @returns Array of unique file paths where skills are defined
739
+ */
740
+ export function getSkillFilePaths(agentMetadata, indexFile) {
741
+ const filePathsSet = new Set();
742
+ const imports = indexFile.getImportDeclarations();
743
+ for (const skill of agentMetadata.skills) {
744
+ if (skill.ref) {
745
+ // Find where this skill is imported from
746
+ for (const importDecl of imports) {
747
+ const namedImports = importDecl.getNamedImports();
748
+ const defaultImport = importDecl.getDefaultImport();
749
+ let isMatch = false;
750
+ // Check named imports
751
+ for (const namedImport of namedImports) {
752
+ if (namedImport.getName() === skill.ref) {
753
+ isMatch = true;
754
+ break;
755
+ }
756
+ }
757
+ // Check default import
758
+ if (!isMatch && defaultImport && defaultImport.getText() === skill.ref) {
759
+ isMatch = true;
760
+ }
761
+ if (isMatch) {
762
+ const moduleSpecifier = importDecl.getModuleSpecifierValue();
763
+ const importPath = resolveImportPath(moduleSpecifier, indexFile.getFilePath());
764
+ if (fs.existsSync(importPath)) {
765
+ filePathsSet.add(importPath); // Use Set to avoid duplicates
766
+ }
767
+ break;
768
+ }
769
+ }
770
+ }
771
+ }
772
+ return Array.from(filePathsSet);
773
+ }
774
+ /**
775
+ * Resolves references in LuaAgent metadata to actual definitions.
776
+ * When a LuaAgent contains references like `skills: [userSkill]`, this function
777
+ * finds where `userSkill` is defined and extracts its metadata.
778
+ */
779
+ export function resolveLuaAgentReferences(agentMetadata, indexFile, project) {
780
+ const resolveArrayReferences = (items, extractFunction) => {
781
+ const resolved = [];
782
+ for (const item of items) {
783
+ if (item.ref) {
784
+ // This is a reference, need to resolve it
785
+ const varName = item.ref;
786
+ // Find the variable declaration in the index file
787
+ const varDecl = findVariableDeclaration(indexFile, varName);
788
+ if (varDecl) {
789
+ // If it's a new expression, extract metadata
790
+ const initializer = varDecl.getInitializer();
791
+ if (initializer && Node.isNewExpression(initializer)) {
792
+ const expression = initializer.getExpression();
793
+ const args = initializer.getArguments();
794
+ if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
795
+ const config = extractConfigFromObjectLiteral(args[0]);
796
+ resolved.push(config);
797
+ }
798
+ }
799
+ }
800
+ else {
801
+ // Check if it's imported from another file
802
+ const importedMetadata = findImportedDefinition(indexFile, varName, project, extractFunction);
803
+ if (importedMetadata) {
804
+ resolved.push(...importedMetadata);
805
+ }
806
+ }
807
+ }
808
+ else {
809
+ // Not a reference, already has metadata
810
+ resolved.push(item);
811
+ }
812
+ }
813
+ return resolved;
814
+ };
815
+ return {
816
+ skills: resolveArrayReferences(agentMetadata.skills, extractSkillsMetadata),
817
+ webhooks: resolveArrayReferences(agentMetadata.webhooks, extractWebhooksMetadata),
818
+ jobs: resolveArrayReferences(agentMetadata.jobs, extractJobsMetadata),
819
+ preProcessors: resolveArrayReferences(agentMetadata.preProcessors, extractPreProcessorsMetadata),
820
+ postProcessors: resolveArrayReferences(agentMetadata.postProcessors, extractPostProcessorsMetadata)
821
+ };
822
+ }
823
+ /**
824
+ * Finds a variable declaration in a source file by name.
825
+ */
826
+ function findVariableDeclaration(sourceFile, varName) {
827
+ const declarations = sourceFile.getVariableDeclarations();
828
+ for (const decl of declarations) {
829
+ if (decl.getName() === varName) {
830
+ return decl;
831
+ }
832
+ }
833
+ return null;
834
+ }
835
+ /**
836
+ * Finds an imported definition and extracts its metadata.
837
+ * This directly extracts the config from the variable declaration in the imported file.
838
+ * Handles both named imports and default imports.
839
+ */
840
+ function findImportedDefinition(sourceFile, varName, project, extractFunction) {
841
+ const imports = sourceFile.getImportDeclarations();
842
+ for (const importDecl of imports) {
843
+ let isMatch = false;
844
+ // Check named imports: import { varName } from '...'
845
+ const namedImports = importDecl.getNamedImports();
846
+ for (const namedImport of namedImports) {
847
+ if (namedImport.getName() === varName) {
848
+ isMatch = true;
849
+ break;
850
+ }
851
+ }
852
+ // Check default import: import varName from '...'
853
+ if (!isMatch) {
854
+ const defaultImport = importDecl.getDefaultImport();
855
+ if (defaultImport && defaultImport.getText() === varName) {
856
+ isMatch = true;
857
+ }
858
+ }
859
+ if (isMatch) {
860
+ // Found the import, resolve the file
861
+ const moduleSpecifier = importDecl.getModuleSpecifierValue();
862
+ const importPath = resolveImportPath(moduleSpecifier, sourceFile.getFilePath());
863
+ if (fs.existsSync(importPath)) {
864
+ try {
865
+ const importedFile = project.addSourceFileAtPath(importPath);
866
+ // Strategy 1: Look for a variable declaration with the same name
867
+ let varDecl = importedFile.getVariableDeclaration(varName);
868
+ // Strategy 2: If it's a default import, look for default export
869
+ if (!varDecl) {
870
+ // Find any variable declaration with default export
871
+ const allVarDecls = importedFile.getVariableDeclarations();
872
+ for (const decl of allVarDecls) {
873
+ // Check if this is exported as default
874
+ const statement = decl.getVariableStatement();
875
+ if (statement && statement.hasDefaultKeyword()) {
876
+ varDecl = decl;
877
+ break;
878
+ }
879
+ }
880
+ // If still not found, just take the first variable declaration
881
+ if (!varDecl && allVarDecls.length > 0) {
882
+ varDecl = allVarDecls[0];
883
+ }
884
+ }
885
+ if (varDecl) {
886
+ const initializer = varDecl.getInitializer();
887
+ // Check if it's a new expression (new LuaSkill, new LuaJob, etc.)
888
+ if (initializer && Node.isNewExpression(initializer)) {
889
+ const args = initializer.getArguments();
890
+ if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
891
+ // Extract configuration from the object literal
892
+ const config = extractConfigFromObjectLiteral(args[0]);
893
+ return [config];
894
+ }
895
+ }
896
+ }
897
+ // Fallback: use the extract function to scan the whole file
898
+ const metadata = extractFunction(importedFile);
899
+ if (metadata.length > 0) {
900
+ // Try to find a match by name
901
+ const matchingItem = metadata.find((item) => {
902
+ return item.name && item.name.toLowerCase().includes(varName.toLowerCase().replace('skill', '').replace('job', '').replace('webhook', ''));
903
+ });
904
+ if (matchingItem) {
905
+ return [matchingItem];
906
+ }
907
+ // Return first item as fallback
908
+ return [metadata[0]];
909
+ }
910
+ }
911
+ catch (error) {
912
+ console.warn(`Warning: Could not resolve import ${varName} from ${importPath}:`, error);
913
+ }
914
+ }
915
+ }
916
+ }
917
+ return null;
918
+ }