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/dist/utils/compile.js
CHANGED
|
@@ -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
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
+
}
|