lua-cli 3.0.2-alpha.2 → 3.0.2-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.
@@ -25,15 +25,12 @@ export default class ProductApi extends HttpClient {
25
25
  * @throws Error if the request fails or products cannot be retrieved
26
26
  */
27
27
  async get(page = 1, limit = 10) {
28
- console.log("get products", page, limit, this.agentId, this.apiKey);
29
28
  const response = await this.httpGet(`/developer/agents/${this.agentId}/products?page=${page}&limit=${limit}`, {
30
29
  Authorization: `Bearer ${this.apiKey}`,
31
30
  });
32
- console.log("get products response", response);
33
31
  if (response.success) {
34
32
  return new ProductPaginationInstance(this, response);
35
33
  }
36
- console.error(response);
37
34
  throw new Error(response.error?.message || "Failed to get products");
38
35
  }
39
36
  /**
@@ -131,11 +131,11 @@ export declare const Products: {
131
131
  /**
132
132
  * Retrieves products with pagination.
133
133
  *
134
- * @param limit - Items per page
135
134
  * @param page - Page number
135
+ * @param limit - Items per page
136
136
  * @returns Promise resolving to product list
137
137
  */
138
- get(limit?: number, page?: number): Promise<ProductPaginationInstance>;
138
+ get(page?: number, limit?: number): Promise<ProductPaginationInstance>;
139
139
  /**
140
140
  * Creates a new product.
141
141
  *
@@ -157,13 +157,13 @@ export const Products = {
157
157
  /**
158
158
  * Retrieves products with pagination.
159
159
  *
160
- * @param limit - Items per page
161
160
  * @param page - Page number
161
+ * @param limit - Items per page
162
162
  * @returns Promise resolving to product list
163
163
  */
164
- async get(limit, page) {
164
+ async get(page, limit) {
165
165
  const instance = await getProductsInstance();
166
- return instance.get(limit, page);
166
+ return instance.get(page, limit);
167
167
  },
168
168
  /**
169
169
  * Creates a new product.
@@ -51,6 +51,7 @@ export function setupSkillCommands(program) {
51
51
  program
52
52
  .command("compile")
53
53
  .description("📦 Compile skill to deployable format")
54
+ .option("--debug", "Enable debug mode with verbose logging and temp file preservation")
54
55
  .action(compileCommand);
55
56
  program
56
57
  .command("test [type]")
@@ -27,6 +27,9 @@
27
27
  * - Skills deleted from code are removed from YAML
28
28
  * - Skills not in YAML are deleted from server (or deactivated if they have versions)
29
29
  *
30
+ * @param options - Command options including debug flag
30
31
  * @returns Promise that resolves when compilation is complete
31
32
  */
32
- export declare function compileCommand(): Promise<void>;
33
+ export declare function compileCommand(options?: {
34
+ debug?: boolean;
35
+ }): Promise<void>;
@@ -43,17 +43,43 @@ import { syncAgentPersonaWithYaml } from '../utils/agent-management.js';
43
43
  * - Skills deleted from code are removed from YAML
44
44
  * - Skills not in YAML are deleted from server (or deactivated if they have versions)
45
45
  *
46
+ * @param options - Command options including debug flag
46
47
  * @returns Promise that resolves when compilation is complete
47
48
  */
48
- export async function compileCommand() {
49
+ export async function compileCommand(options) {
49
50
  return withErrorHandling(async () => {
51
+ const debugMode = options?.debug || process.env.LUA_DEBUG === 'true';
52
+ if (debugMode) {
53
+ console.log('🐛 Debug mode enabled');
54
+ console.log(' - Verbose logging: ON');
55
+ console.log(' - Temp file preservation: ON');
56
+ console.log(' - Full error stacks: ON\n');
57
+ }
50
58
  writeProgress("🔨 Compiling Lua skill...");
59
+ // Track compilation issues
60
+ const compilationWarnings = [];
61
+ const failedBundles = [];
62
+ const startTime = Date.now();
51
63
  // Step 1: Prepare output directories
64
+ if (debugMode)
65
+ console.log('📁 Preparing output directories...');
52
66
  const { distDir, luaDir } = prepareOutputDirectories();
67
+ if (debugMode)
68
+ console.log(` ✓ Created: ${distDir}`);
69
+ if (debugMode)
70
+ console.log(` ✓ Created: ${luaDir}\n`);
53
71
  // Step 2: Analyze TypeScript project and detect tools
72
+ if (debugMode)
73
+ console.log('🔍 Finding index file...');
54
74
  const indexPath = findIndexFile();
75
+ if (debugMode)
76
+ console.log(` ✓ Found: ${indexPath}\n`);
77
+ if (debugMode)
78
+ console.log('📚 Creating TypeScript project...');
55
79
  const project = createTypeScriptProject();
56
80
  const indexFile = project.addSourceFileAtPath(indexPath);
81
+ if (debugMode)
82
+ console.log(` ✓ Loaded source file\n`);
57
83
  // Step 2a: Check for LuaAgent (unified agent configuration)
58
84
  writeProgress("🔍 Checking for LuaAgent configuration...");
59
85
  const { extractLuaAgentMetadata, resolveLuaAgentReferences, getSkillFilePaths } = await import('../utils/compile.js');
@@ -69,7 +95,7 @@ export async function compileCommand() {
69
95
  resolvedAgentData = resolveLuaAgentReferences(agentMetadata, indexFile, project);
70
96
  // Get file paths where skills are defined so we can scan them for tools
71
97
  skillFilePaths = getSkillFilePaths(agentMetadata, indexFile);
72
- writeProgress(`📦 Agent contains: ${resolvedAgentData.skills.length} skill(s), ${resolvedAgentData.webhooks.length} webhook(s), ${resolvedAgentData.jobs.length} job(s), ${resolvedAgentData.preProcessors.length} preprocessor(s), ${resolvedAgentData.postProcessors.length} postprocessor(s)`);
98
+ writeProgress(`📦 Agent contains: ${resolvedAgentData?.skills?.length || 0} skill(s), ${resolvedAgentData?.webhooks?.length || 0} webhook(s), ${resolvedAgentData?.jobs?.length || 0} job(s), ${resolvedAgentData?.preProcessors?.length || 0} preprocessor(s), ${resolvedAgentData?.postProcessors?.length || 0} postprocessor(s)`);
73
99
  }
74
100
  else {
75
101
  writeProgress(`ℹ️ No LuaAgent found, using legacy detection for individual components`);
@@ -80,21 +106,41 @@ export async function compileCommand() {
80
106
  // Step 3: Bundle each tool and extract metadata
81
107
  const { preBundleJobsInSource, replaceJobPlaceholders } = await import('../utils/pre-bundle-jobs.js');
82
108
  for (const tool of tools) {
109
+ if (debugMode)
110
+ console.log(`\n🔧 Processing tool: ${tool.className}`);
111
+ if (debugMode)
112
+ console.log(` File: ${tool.filePath}`);
83
113
  // Step 3a: Pre-bundle any Jobs.create() in the tool source
114
+ const toolStartTime = Date.now();
84
115
  const { modifiedSource, jobBundles } = await preBundleJobsInSource(tool.filePath, project, distDir);
116
+ if (debugMode && jobBundles.size > 0) {
117
+ console.log(` ✓ Found ${jobBundles.size} nested job(s) to pre-bundle`);
118
+ }
85
119
  // Step 3b: Bundle the tool (with placeholders for job execute functions)
86
- await bundleTool(tool, distDir, modifiedSource);
120
+ await bundleTool(tool, distDir, modifiedSource, debugMode);
87
121
  // Step 3c: Replace placeholders in the bundled tool file
88
122
  if (jobBundles.size > 0) {
89
123
  const toolBundlePath = path.join(distDir, 'tools', `${tool.className}.js`);
90
124
  if (fs.existsSync(toolBundlePath)) {
91
125
  let bundledToolCode = fs.readFileSync(toolBundlePath, 'utf8');
126
+ const beforeSize = bundledToolCode.length;
92
127
  bundledToolCode = replaceJobPlaceholders(bundledToolCode, jobBundles);
128
+ const afterSize = bundledToolCode.length;
129
+ if (debugMode)
130
+ console.log(` ✓ Replaced placeholders (+${afterSize - beforeSize} bytes)`);
93
131
  fs.writeFileSync(toolBundlePath, bundledToolCode);
94
132
  }
95
133
  }
96
134
  // Step 3d: Extract execute code
97
135
  await extractExecuteCode(tool, project, distDir);
136
+ if (debugMode) {
137
+ const toolTime = Date.now() - toolStartTime;
138
+ const bundledPath = path.join(distDir, 'tools', `${tool.className}.js`);
139
+ if (fs.existsSync(bundledPath)) {
140
+ const size = fs.statSync(bundledPath).size;
141
+ console.log(` ✓ Bundled: ${(size / 1024).toFixed(2)}KB in ${toolTime}ms`);
142
+ }
143
+ }
98
144
  }
99
145
  // Step 4: Bundle the main index file
100
146
  await bundleMainIndex(indexPath, distDir);
@@ -119,8 +165,15 @@ export async function compileCommand() {
119
165
  writeProgress(`📦 Found ${webhooksMetadata.length} webhook(s)...`);
120
166
  // Bundle each webhook (extract execute function and schemas)
121
167
  for (const webhook of webhooksMetadata) {
122
- const bundled = await bundleWebhook(webhook, indexFile, distDir, project);
168
+ if (debugMode)
169
+ console.log(`\n🌐 Processing webhook: ${webhook.name}`);
170
+ const webhookStartTime = Date.now();
171
+ const bundled = await bundleWebhook(webhook, indexFile, distDir, project, debugMode);
123
172
  bundledWebhooks.push(bundled);
173
+ if (debugMode) {
174
+ const webhookTime = Date.now() - webhookStartTime;
175
+ console.log(` ✓ Completed in ${webhookTime}ms`);
176
+ }
124
177
  }
125
178
  // Ensure webhooks exist in YAML with valid IDs
126
179
  const configForWebhooks = readSkillConfig(); // Re-read for webhooks
@@ -141,8 +194,15 @@ export async function compileCommand() {
141
194
  writeProgress(`📦 Found ${jobsMetadata.length} job(s)...`);
142
195
  // Bundle each job (extract execute function)
143
196
  for (const job of jobsMetadata) {
144
- const bundled = await bundleJob(job, indexFile, distDir, project);
197
+ if (debugMode)
198
+ console.log(`\n⚙️ Processing job: ${job.name}`);
199
+ const jobStartTime = Date.now();
200
+ const bundled = await bundleJob(job, indexFile, distDir, project, debugMode);
145
201
  bundledJobs.push(bundled);
202
+ if (debugMode) {
203
+ const jobTime = Date.now() - jobStartTime;
204
+ console.log(` ✓ Completed in ${jobTime}ms`);
205
+ }
146
206
  }
147
207
  // Ensure jobs exist in YAML with valid IDs
148
208
  const configForJobs = readSkillConfig(); // Re-read for jobs
@@ -162,7 +222,9 @@ export async function compileCommand() {
162
222
  if (preprocessorsMetadata.length > 0) {
163
223
  writeProgress(`📦 Found ${preprocessorsMetadata.length} preprocessor(s)...`);
164
224
  for (const preprocessor of preprocessorsMetadata) {
165
- const bundled = await bundlePreProcessor(preprocessor, indexFile, distDir, project);
225
+ if (debugMode)
226
+ console.log(`\n⚡ Processing preprocessor: ${preprocessor.name}`);
227
+ const bundled = await bundlePreProcessor(preprocessor, indexFile, distDir, project, debugMode);
166
228
  bundledPreProcessors.push(bundled);
167
229
  }
168
230
  // Ensure preprocessors exist in YAML with valid IDs
@@ -182,7 +244,9 @@ export async function compileCommand() {
182
244
  if (postprocessorsMetadata.length > 0) {
183
245
  writeProgress(`📦 Found ${postprocessorsMetadata.length} postprocessor(s)...`);
184
246
  for (const postprocessor of postprocessorsMetadata) {
185
- const bundled = await bundlePostProcessor(postprocessor, indexFile, distDir, project);
247
+ if (debugMode)
248
+ console.log(`\n⚡ Processing postprocessor: ${postprocessor.name}`);
249
+ const bundled = await bundlePostProcessor(postprocessor, indexFile, distDir, project, debugMode);
186
250
  bundledPostProcessors.push(bundled);
187
251
  }
188
252
  // Ensure postprocessors exist in YAML with valid IDs
@@ -194,6 +258,7 @@ export async function compileCommand() {
194
258
  await syncServerPostProcessorsWithYaml(postprocessorConfig);
195
259
  fs.writeFileSync(path.join(distDir, 'postprocessors.json'), JSON.stringify(bundledPostProcessors, null, 2));
196
260
  }
261
+ // Build compilation summary
197
262
  const summaryParts = [`${tools.length} tools bundled`];
198
263
  if (webhooksMetadata.length > 0)
199
264
  summaryParts.push(`${webhooksMetadata.length} webhook(s) registered`);
@@ -203,6 +268,36 @@ export async function compileCommand() {
203
268
  summaryParts.push(`${preprocessorsMetadata.length} preprocessor(s) registered`);
204
269
  if (postprocessorsMetadata.length > 0)
205
270
  summaryParts.push(`${postprocessorsMetadata.length} postprocessor(s) registered`);
271
+ // Check for empty bundles (potential failures)
272
+ const emptyBundles = [];
273
+ for (const tool of tools) {
274
+ const bundledPath = path.join(distDir, 'tools', `${tool.className}.js`);
275
+ if (fs.existsSync(bundledPath)) {
276
+ const size = fs.statSync(bundledPath).size;
277
+ if (size < 100) { // Less than 100 bytes is suspiciously small
278
+ emptyBundles.push(`${tool.className} (${size} bytes)`);
279
+ }
280
+ }
281
+ else {
282
+ emptyBundles.push(`${tool.className} (file missing)`);
283
+ }
284
+ }
285
+ if (emptyBundles.length > 0) {
286
+ console.warn(`\n⚠️ Warning: ${emptyBundles.length} tool(s) may have bundling issues:`);
287
+ emptyBundles.forEach(name => console.warn(` - ${name}`));
288
+ console.warn(` Run with NODE_ENV=development for detailed error logs.\n`);
289
+ }
290
+ const totalTime = Date.now() - startTime;
291
+ if (debugMode) {
292
+ console.log(`\n⏱️ Total compilation time: ${(totalTime / 1000).toFixed(2)}s`);
293
+ console.log(`📊 Output directories:`);
294
+ console.log(` - ${distDir}`);
295
+ console.log(` - ${luaDir}`);
296
+ const tempDir = path.join(distDir, '.temp');
297
+ if (fs.existsSync(tempDir)) {
298
+ console.log(` - ${tempDir} (preserved for debugging)`);
299
+ }
300
+ }
206
301
  writeSuccess(`✅ Skill compiled successfully - ${summaryParts.join(', ')}`);
207
302
  }, "compilation");
208
303
  }
@@ -230,11 +325,22 @@ function prepareOutputDirectories() {
230
325
  }
231
326
  /**
232
327
  * Creates and configures a TypeScript project for AST analysis.
328
+ * Validates that tsconfig.json exists before creating project.
233
329
  *
234
330
  * @returns Configured ts-morph Project instance
331
+ * @throws Error if tsconfig.json is missing or invalid
235
332
  */
236
333
  function createTypeScriptProject() {
237
- return new Project({
238
- tsConfigFilePath: path.join(process.cwd(), COMPILE_FILES.TSCONFIG_JSON),
239
- });
334
+ const tsconfigPath = path.join(process.cwd(), COMPILE_FILES.TSCONFIG_JSON);
335
+ if (!fs.existsSync(tsconfigPath)) {
336
+ throw new Error(`tsconfig.json not found at ${tsconfigPath}\n` +
337
+ `Please ensure you're in a Lua CLI project directory.`);
338
+ }
339
+ try {
340
+ return new Project({ tsConfigFilePath: tsconfigPath });
341
+ }
342
+ catch (error) {
343
+ throw new Error(`Failed to parse tsconfig.json: ${error.message}\n` +
344
+ `Please check that your tsconfig.json is valid.`);
345
+ }
240
346
  }
package/dist/index.js CHANGED
@@ -10,16 +10,24 @@
10
10
  */
11
11
  import { Command } from "commander";
12
12
  import { setupAuthCommands, setupSkillCommands } from "./cli/command-definitions.js";
13
+ import { readFileSync } from "fs";
14
+ import { fileURLToPath } from "url";
15
+ import { dirname, join } from "path";
16
+ // Get version from package.json
17
+ const __filename = fileURLToPath(import.meta.url);
18
+ const __dirname = dirname(__filename);
19
+ const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf8'));
20
+ const CLI_VERSION = packageJson.version;
13
21
  // Create the main CLI program
14
22
  const program = new Command();
15
23
  // Configure program metadata
16
24
  program
17
25
  .name("lua")
18
26
  .description("Lua AI - Build and deploy AI agents with superpowers")
19
- .version("2.5.8")
27
+ .version(CLI_VERSION)
20
28
  .addHelpText('before', `
21
29
  ------------------------------------------------------------------
22
- Lua AI CLI v2.5.8 - Build and deploy AI agents with superpowers
30
+ Lua AI CLI v${CLI_VERSION} - Build and deploy AI agents with superpowers
23
31
  ------------------------------------------------------------------
24
32
  `)
25
33
  .addHelpText('after', `
@@ -22,8 +22,10 @@ export declare const sandboxGlobalsPlugin: Plugin;
22
22
  *
23
23
  * @param tool - The tool to bundle
24
24
  * @param distDir - The distribution directory for output
25
+ * @param modifiedSource - Optional modified source (for pre-bundled jobs)
26
+ * @param debugMode - Enable verbose logging and preserve temp files
25
27
  */
26
- export declare function bundleTool(tool: ToolInfo, distDir: string, modifiedSource?: string): Promise<void>;
28
+ export declare function bundleTool(tool: ToolInfo, distDir: string, modifiedSource?: string, debugMode?: boolean): Promise<void>;
27
29
  /**
28
30
  * Bundles the main index.ts file into a standalone JavaScript file.
29
31
  * This creates the entry point for the skill.
@@ -39,9 +41,10 @@ export declare function bundleMainIndex(indexPath: string, distDir: string): Pro
39
41
  * @param indexFile - The TypeScript source file containing the webhook
40
42
  * @param distDir - Distribution directory for output
41
43
  * @param project - Optional ts-morph Project for resolving imports
44
+ * @param debugMode - Enable verbose logging and preserve temp files
42
45
  * @returns Webhook with bundled code and schemas
43
46
  */
44
- export declare function bundleWebhook(webhook: any, indexFile: any, distDir: string, project?: any): Promise<any>;
47
+ export declare function bundleWebhook(webhook: any, indexFile: any, distDir: string, project?: any, debugMode?: boolean): Promise<any>;
45
48
  /**
46
49
  * Extracts and bundles job execute function.
47
50
  *
@@ -49,17 +52,18 @@ export declare function bundleWebhook(webhook: any, indexFile: any, distDir: str
49
52
  * @param indexFile - The TypeScript source file containing the job
50
53
  * @param distDir - Distribution directory for output
51
54
  * @param project - Optional ts-morph Project for resolving imports
55
+ * @param debugMode - Enable verbose logging and preserve temp files
52
56
  * @returns Job with bundled code
53
57
  */
54
- export declare function bundleJob(job: any, indexFile: any, distDir: string, project?: any): Promise<any>;
58
+ export declare function bundleJob(job: any, indexFile: any, distDir: string, project?: any, debugMode?: boolean): Promise<any>;
55
59
  /**
56
60
  * Bundles and compresses preprocessor execute function code.
57
61
  */
58
- export declare function bundlePreProcessor(preprocessor: any, indexFile: any, distDir: string, project?: any): Promise<any>;
62
+ export declare function bundlePreProcessor(preprocessor: any, indexFile: any, distDir: string, project?: any, debugMode?: boolean): Promise<any>;
59
63
  /**
60
64
  * Bundles and compresses postprocessor execute function code.
61
65
  */
62
- export declare function bundlePostProcessor(postprocessor: any, indexFile: any, distDir: string, project?: any): Promise<any>;
66
+ export declare function bundlePostProcessor(postprocessor: any, indexFile: any, distDir: string, project?: any, debugMode?: boolean): Promise<any>;
63
67
  /**
64
68
  * Extracts execute code and input schema from a tool.
65
69
  * This function:
@@ -63,9 +63,10 @@ function extractRelevantImports(sourceFilePath) {
63
63
  * @param vmWrapperGenerator - Function that generates the VM wrapper code
64
64
  * @param distDir - Distribution directory
65
65
  * @param sourceFilePath - Path to source file containing the component
66
+ * @param debugMode - Enable verbose logging and preserve temp files
66
67
  * @returns Compressed base64-encoded bundled code
67
68
  */
68
- async function bundleAndCompressExecuteFunction(executeFunction, componentName, componentType, vmWrapperGenerator, distDir, sourceFilePath) {
69
+ async function bundleAndCompressExecuteFunction(executeFunction, componentName, componentType, vmWrapperGenerator, distDir, sourceFilePath, debugMode) {
69
70
  const { compressCode } = await import('./compile.js');
70
71
  // Create temporary file with the execute function wrapped as a module
71
72
  const tempDir = path.join(distDir, '.temp');
@@ -77,6 +78,12 @@ async function bundleAndCompressExecuteFunction(executeFunction, componentName,
77
78
  try {
78
79
  // Extract relevant imports from source file
79
80
  const relevantImports = extractRelevantImports(sourceFilePath);
81
+ if (debugMode) {
82
+ console.log(` → Found ${relevantImports.length} import(s) to bundle`);
83
+ if (relevantImports.length > 0 && debugMode) {
84
+ relevantImports.forEach(imp => console.log(` - ${imp}`));
85
+ }
86
+ }
80
87
  // Write execute function as a module export with all relevant imports
81
88
  const moduleCode = `
82
89
  // ${componentType} execute function for ${componentName}
@@ -88,6 +95,9 @@ const executeFunc = ${executeFunction};
88
95
  export default executeFunc;
89
96
  `;
90
97
  fs.writeFileSync(tempFile, moduleCode);
98
+ if (debugMode) {
99
+ console.log(` → Created temp file: ${path.basename(tempFile)}`);
100
+ }
91
101
  // Bundle with esbuild using the source file's directory for import resolution
92
102
  await build({
93
103
  entryPoints: [tempFile],
@@ -101,33 +111,71 @@ export default executeFunc;
101
111
  plugins: [sandboxGlobalsPlugin],
102
112
  absWorkingDir: path.dirname(sourceFilePath), // Use source file's directory for resolution
103
113
  });
104
- // Read bundled code
114
+ // Read bundled code and validate it exists and has content
115
+ if (!fs.existsSync(tempOutput)) {
116
+ throw new Error(`esbuild failed to create output file for ${componentName}`);
117
+ }
105
118
  const bundledCode = fs.readFileSync(tempOutput, 'utf8');
119
+ if (bundledCode.length === 0) {
120
+ throw new Error(`esbuild created empty bundle for ${componentName}`);
121
+ }
106
122
  // Wrap for VM execution using provided generator
107
123
  const wrappedCode = vmWrapperGenerator(bundledCode);
108
124
  // Compress the wrapped code
109
125
  const compressed = compressCode(wrappedCode);
110
- // Clean up temp files
111
- try {
112
- fs.unlinkSync(tempFile);
113
- fs.unlinkSync(tempOutput);
126
+ if (debugMode) {
127
+ console.log(` → Bundle size: ${(bundledCode.length / 1024).toFixed(2)}KB (uncompressed)`);
128
+ console.log(` → Compressed: ${(compressed.length / 1024).toFixed(2)}KB (base64 gzip)`);
114
129
  }
115
- catch (cleanupError) {
116
- // Ignore cleanup errors
130
+ // Clean up temp files (unless in debug mode)
131
+ if (!debugMode) {
132
+ try {
133
+ fs.unlinkSync(tempFile);
134
+ fs.unlinkSync(tempOutput);
135
+ }
136
+ catch (cleanupError) {
137
+ // Ignore cleanup errors
138
+ }
139
+ }
140
+ else {
141
+ console.log(` ℹ️ Preserved temp files for debugging:`);
142
+ console.log(` - ${tempFile}`);
143
+ console.log(` - ${tempOutput}`);
117
144
  }
118
145
  return compressed;
119
146
  }
120
147
  catch (error) {
121
- console.warn(`Warning: Could not bundle ${componentType} ${componentName} code:`, error);
122
- // Clean up on error
123
- try {
124
- if (fs.existsSync(tempFile))
125
- fs.unlinkSync(tempFile);
126
- if (fs.existsSync(tempOutput))
127
- fs.unlinkSync(tempOutput);
148
+ // Provide helpful error messages based on error type
149
+ let errorMessage = `Warning: Could not bundle ${componentType} ${componentName}`;
150
+ if (error.message && error.message.includes('Could not resolve')) {
151
+ errorMessage += `\n Dependency resolution failed: ${error.message}`;
152
+ errorMessage += `\n Hint: Ensure all imported packages are in package.json and run 'npm install'`;
153
+ }
154
+ else if (error.message && error.message.includes('Transform failed')) {
155
+ errorMessage += `\n TypeScript compilation failed: ${error.message}`;
156
+ errorMessage += `\n Hint: Check syntax in ${path.basename(sourceFilePath)}`;
157
+ }
158
+ else {
159
+ errorMessage += `: ${error.message || error}`;
160
+ }
161
+ console.warn(errorMessage);
162
+ if (debugMode && error.stack) {
163
+ console.error(' Full stack trace:', error.stack);
164
+ }
165
+ // Clean up on error (unless in debug mode)
166
+ if (!debugMode) {
167
+ try {
168
+ if (fs.existsSync(tempFile))
169
+ fs.unlinkSync(tempFile);
170
+ if (fs.existsSync(tempOutput))
171
+ fs.unlinkSync(tempOutput);
172
+ }
173
+ catch (cleanupError) {
174
+ // Ignore cleanup errors
175
+ }
128
176
  }
129
- catch (cleanupError) {
130
- // Ignore cleanup errors
177
+ else {
178
+ console.log(` ℹ️ Temp files preserved for debugging (check dist/.temp/)`);
131
179
  }
132
180
  return '';
133
181
  }
@@ -211,9 +259,14 @@ export const sandboxGlobalsPlugin = {
211
259
  *
212
260
  * @param tool - The tool to bundle
213
261
  * @param distDir - The distribution directory for output
262
+ * @param modifiedSource - Optional modified source (for pre-bundled jobs)
263
+ * @param debugMode - Enable verbose logging and preserve temp files
214
264
  */
215
- export async function bundleTool(tool, distDir, modifiedSource) {
216
- writeProgress(`📦 Bundling ${tool.className}...`);
265
+ export async function bundleTool(tool, distDir, modifiedSource, debugMode) {
266
+ if (!debugMode)
267
+ writeProgress(`📦 Bundling ${tool.className}...`);
268
+ if (debugMode)
269
+ console.log(` → Bundling ${tool.className}...`);
217
270
  try {
218
271
  const outputPath = path.join(distDir, COMPILE_DIRS.TOOLS, `${tool.className}.js`);
219
272
  let entryPoint = tool.filePath;
@@ -277,8 +330,8 @@ export async function bundleTool(tool, distDir, modifiedSource) {
277
330
  }
278
331
  // Wrap the bundled code for VM execution environment
279
332
  await wrapToolForVM(outputPath, tool);
280
- // Clean up temp file if created
281
- if (tempFile && fs.existsSync(tempFile)) {
333
+ // Clean up temp file if created (unless in debug mode)
334
+ if (tempFile && fs.existsSync(tempFile) && !debugMode) {
282
335
  try {
283
336
  fs.unlinkSync(tempFile);
284
337
  }
@@ -286,9 +339,16 @@ export async function bundleTool(tool, distDir, modifiedSource) {
286
339
  // Ignore cleanup errors
287
340
  }
288
341
  }
342
+ else if (tempFile && debugMode) {
343
+ if (debugMode)
344
+ console.log(` ℹ️ Preserved temp file: ${tempFile}`);
345
+ }
289
346
  }
290
347
  catch (error) {
291
348
  console.warn(`Warning: Failed to bundle ${tool.className}:`, error);
349
+ if (debugMode && error instanceof Error) {
350
+ console.error(' Stack trace:', error.stack);
351
+ }
292
352
  }
293
353
  }
294
354
  /**
@@ -318,10 +378,12 @@ export async function bundleMainIndex(indexPath, distDir) {
318
378
  * @param indexFile - The TypeScript source file containing the webhook
319
379
  * @param distDir - Distribution directory for output
320
380
  * @param project - Optional ts-morph Project for resolving imports
381
+ * @param debugMode - Enable verbose logging and preserve temp files
321
382
  * @returns Webhook with bundled code and schemas
322
383
  */
323
- export async function bundleWebhook(webhook, indexFile, distDir, project) {
324
- writeProgress(`📦 Bundling webhook ${webhook.name}...`);
384
+ export async function bundleWebhook(webhook, indexFile, distDir, project, debugMode) {
385
+ if (!debugMode)
386
+ writeProgress(`📦 Bundling webhook ${webhook.name}...`);
325
387
  try {
326
388
  // Find the LuaWebhook constructor in the AST
327
389
  let executeFunction = '';
@@ -420,7 +482,7 @@ export async function bundleWebhook(webhook, indexFile, distDir, project) {
420
482
  // Bundle and compress the execute function (like tools)
421
483
  let compressedCode = '';
422
484
  if (executeFunction) {
423
- compressedCode = await bundleAndCompressWebhookCode(executeFunction, webhook.name, distDir, sourceFilePath);
485
+ compressedCode = await bundleAndCompressWebhookCode(executeFunction, webhook.name, distDir, sourceFilePath, debugMode);
424
486
  }
425
487
  return {
426
488
  ...webhook,
@@ -443,9 +505,10 @@ export async function bundleWebhook(webhook, indexFile, distDir, project) {
443
505
  * @param webhookName - Name of the webhook
444
506
  * @param distDir - Distribution directory
445
507
  * @param sourceFilePath - Path to the source file containing the webhook
508
+ * @param debugMode - Enable verbose logging and preserve temp files
446
509
  * @returns Compressed base64-encoded bundled code
447
510
  */
448
- async function bundleAndCompressWebhookCode(executeFunction, webhookName, distDir, sourceFilePath) {
511
+ async function bundleAndCompressWebhookCode(executeFunction, webhookName, distDir, sourceFilePath, debugMode) {
449
512
  return bundleAndCompressExecuteFunction(executeFunction, webhookName, 'webhook', (bundledCode) => `(async (query, headers, body) => {
450
513
 
451
514
  // Execute the bundled webhook code
@@ -456,7 +519,7 @@ async function bundleAndCompressWebhookCode(executeFunction, webhookName, distDi
456
519
 
457
520
  // Execute with three separate parameters (not an object)
458
521
  return await executeFunction(query, headers, body);
459
- })`, distDir, sourceFilePath);
522
+ })`, distDir, sourceFilePath, debugMode);
460
523
  }
461
524
  /**
462
525
  * Extracts and bundles job execute function.
@@ -465,10 +528,12 @@ async function bundleAndCompressWebhookCode(executeFunction, webhookName, distDi
465
528
  * @param indexFile - The TypeScript source file containing the job
466
529
  * @param distDir - Distribution directory for output
467
530
  * @param project - Optional ts-morph Project for resolving imports
531
+ * @param debugMode - Enable verbose logging and preserve temp files
468
532
  * @returns Job with bundled code
469
533
  */
470
- export async function bundleJob(job, indexFile, distDir, project) {
471
- writeProgress(`📦 Bundling job ${job.name}...`);
534
+ export async function bundleJob(job, indexFile, distDir, project, debugMode) {
535
+ if (!debugMode)
536
+ writeProgress(`📦 Bundling job ${job.name}...`);
472
537
  try {
473
538
  // Find the LuaJob constructor in the AST
474
539
  let executeFunction = '';
@@ -538,7 +603,7 @@ export async function bundleJob(job, indexFile, distDir, project) {
538
603
  // Bundle and compress the execute function (like tools)
539
604
  let compressedCode = '';
540
605
  if (executeFunction) {
541
- compressedCode = await bundleAndCompressJobCode(executeFunction, job.name, distDir, sourceFilePath);
606
+ compressedCode = await bundleAndCompressJobCode(executeFunction, job.name, distDir, sourceFilePath, debugMode);
542
607
  }
543
608
  return {
544
609
  ...job,
@@ -560,9 +625,10 @@ export async function bundleJob(job, indexFile, distDir, project) {
560
625
  * @param jobName - Name of the job
561
626
  * @param distDir - Distribution directory
562
627
  * @param sourceFilePath - Path to the source file containing the job
628
+ * @param debugMode - Enable verbose logging and preserve temp files
563
629
  * @returns Compressed base64-encoded bundled code
564
630
  */
565
- async function bundleAndCompressJobCode(executeFunction, jobName, distDir, sourceFilePath) {
631
+ async function bundleAndCompressJobCode(executeFunction, jobName, distDir, sourceFilePath, debugMode) {
566
632
  return bundleAndCompressExecuteFunction(executeFunction, jobName, 'job', (bundledCode) => `(async (job) => {
567
633
  // Execute the bundled job code
568
634
  ${bundledCode}
@@ -572,13 +638,14 @@ async function bundleAndCompressJobCode(executeFunction, jobName, distDir, sourc
572
638
 
573
639
  // Execute job with job instance parameter
574
640
  return await executeFunction(job);
575
- })`, distDir, sourceFilePath);
641
+ })`, distDir, sourceFilePath, debugMode);
576
642
  }
577
643
  /**
578
644
  * Bundles and compresses preprocessor execute function code.
579
645
  */
580
- export async function bundlePreProcessor(preprocessor, indexFile, distDir, project) {
581
- writeProgress(`📦 Bundling preprocessor ${preprocessor.name}...`);
646
+ export async function bundlePreProcessor(preprocessor, indexFile, distDir, project, debugMode) {
647
+ if (!debugMode)
648
+ writeProgress(`📦 Bundling preprocessor ${preprocessor.name}...`);
582
649
  try {
583
650
  let executeFunction = '';
584
651
  let sourceFilePath = indexFile.getFilePath();
@@ -642,7 +709,7 @@ export async function bundlePreProcessor(preprocessor, indexFile, distDir, proje
642
709
  }
643
710
  let compressedCode = '';
644
711
  if (executeFunction) {
645
- compressedCode = await bundleAndCompressProcessorCode(executeFunction, preprocessor.name, 'pre', distDir, sourceFilePath);
712
+ compressedCode = await bundleAndCompressProcessorCode(executeFunction, preprocessor.name, 'pre', distDir, sourceFilePath, debugMode);
646
713
  }
647
714
  return {
648
715
  name: preprocessor.name,
@@ -662,8 +729,9 @@ export async function bundlePreProcessor(preprocessor, indexFile, distDir, proje
662
729
  /**
663
730
  * Bundles and compresses postprocessor execute function code.
664
731
  */
665
- export async function bundlePostProcessor(postprocessor, indexFile, distDir, project) {
666
- writeProgress(`📦 Bundling postprocessor ${postprocessor.name}...`);
732
+ export async function bundlePostProcessor(postprocessor, indexFile, distDir, project, debugMode) {
733
+ if (!debugMode)
734
+ writeProgress(`📦 Bundling postprocessor ${postprocessor.name}...`);
667
735
  try {
668
736
  let executeFunction = '';
669
737
  let sourceFilePath = indexFile.getFilePath();
@@ -727,7 +795,7 @@ export async function bundlePostProcessor(postprocessor, indexFile, distDir, pro
727
795
  }
728
796
  let compressedCode = '';
729
797
  if (executeFunction) {
730
- compressedCode = await bundleAndCompressProcessorCode(executeFunction, postprocessor.name, 'post', distDir, sourceFilePath);
798
+ compressedCode = await bundleAndCompressProcessorCode(executeFunction, postprocessor.name, 'post', distDir, sourceFilePath, debugMode);
731
799
  }
732
800
  return {
733
801
  name: postprocessor.name,
@@ -748,7 +816,7 @@ export async function bundlePostProcessor(postprocessor, indexFile, distDir, pro
748
816
  * Bundles and compresses processor execute function code.
749
817
  * Includes all imports from the source file to ensure dependencies are bundled.
750
818
  */
751
- async function bundleAndCompressProcessorCode(executeFunction, processorName, type, distDir, sourceFilePath) {
819
+ async function bundleAndCompressProcessorCode(executeFunction, processorName, type, distDir, sourceFilePath, debugMode) {
752
820
  // Processor execute functions receive: (user, message, [response], channel)
753
821
  const paramList = type === 'pre' ? 'user, message, channel' : 'user, message, response, channel';
754
822
  return bundleAndCompressExecuteFunction(executeFunction, processorName, `${type}processor`, (bundledCode) => `(async (${paramList}) => {
@@ -757,7 +825,7 @@ async function bundleAndCompressProcessorCode(executeFunction, processorName, ty
757
825
 
758
826
  const executeFunction = module.exports.default || module.exports;
759
827
  return await executeFunction(${paramList});
760
- })`, distDir, sourceFilePath);
828
+ })`, distDir, sourceFilePath, debugMode);
761
829
  }
762
830
  /**
763
831
  * Extracts execute code and input schema from a tool.
@@ -12,7 +12,10 @@ export declare function compressCode(code: string): string;
12
12
  */
13
13
  export declare function findIndexFile(): string;
14
14
  /**
15
- * Resolves import path from module specifier
15
+ * Resolves import path from module specifier with support for multiple extensions and index files
16
+ * @param moduleSpecifier - The import path (e.g., './tools/MyTool' or '../utils')
17
+ * @param currentFilePath - The file doing the importing
18
+ * @returns Resolved absolute file path
16
19
  */
17
20
  export declare function resolveImportPath(moduleSpecifier: string, currentFilePath: string): string;
18
21
  /**
@@ -29,16 +29,37 @@ export function findIndexFile() {
29
29
  throw new Error("index.ts not found in current directory or src/ directory");
30
30
  }
31
31
  /**
32
- * Resolves import path from module specifier
32
+ * Resolves import path from module specifier with support for multiple extensions and index files
33
+ * @param moduleSpecifier - The import path (e.g., './tools/MyTool' or '../utils')
34
+ * @param currentFilePath - The file doing the importing
35
+ * @returns Resolved absolute file path
33
36
  */
34
37
  export function resolveImportPath(moduleSpecifier, currentFilePath) {
38
+ const extensions = ['.ts', '.tsx', '.js', '.jsx', '/index.ts', '/index.tsx', '/index.js'];
35
39
  if (moduleSpecifier.startsWith('./') || moduleSpecifier.startsWith('../')) {
36
40
  // Relative import - resolve relative to current file
37
41
  const currentDir = path.dirname(currentFilePath);
38
- return path.resolve(currentDir, moduleSpecifier + '.ts');
42
+ const basePath = path.resolve(currentDir, moduleSpecifier);
43
+ // Try each extension in order
44
+ for (const ext of extensions) {
45
+ const fullPath = ext.startsWith('/') ? path.join(basePath, ext) : basePath + ext;
46
+ if (fs.existsSync(fullPath)) {
47
+ return fullPath;
48
+ }
49
+ }
50
+ // Fallback: return .ts extension (original behavior)
51
+ return basePath + '.ts';
39
52
  }
40
53
  else {
41
- // Absolute import - assume it's in the project
54
+ // Absolute import - check in src/ directory
55
+ const srcBase = path.resolve(process.cwd(), 'src', moduleSpecifier);
56
+ for (const ext of extensions) {
57
+ const fullPath = ext.startsWith('/') ? path.join(srcBase, ext) : srcBase + ext;
58
+ if (fs.existsSync(fullPath)) {
59
+ return fullPath;
60
+ }
61
+ }
62
+ // Fallback: return .ts in src/ (original behavior)
42
63
  return path.resolve(process.cwd(), 'src', moduleSpecifier + '.ts');
43
64
  }
44
65
  }
@@ -56,6 +56,10 @@ function readPackageJson() {
56
56
  */
57
57
  export async function createLegacyDeploymentData(tools, luaDir, indexFile, agentData) {
58
58
  const config = readSkillConfig();
59
+ // Handle null config gracefully
60
+ if (!config) {
61
+ console.warn('⚠️ Warning: lua.skill.yaml not found. Creating deployment with default configuration.');
62
+ }
59
63
  let skillsMetadata;
60
64
  // Check if we have agent data (from LuaAgent approach)
61
65
  if (agentData && agentData.skills && agentData.skills.length > 0) {
@@ -75,9 +79,9 @@ export async function createLegacyDeploymentData(tools, luaDir, indexFile, agent
75
79
  // Build skills array with their associated tools
76
80
  const skillsArray = buildSkillsArray(skillsMetadata, skillToTools, tools);
77
81
  // Ensure all skills exist in YAML config and have valid IDs
78
- const updatedSkillsArray = await ensureSkillsExistInYaml(skillsArray, config);
82
+ const updatedSkillsArray = config ? await ensureSkillsExistInYaml(skillsArray, config) : skillsArray;
79
83
  // Override versions from YAML config if they exist
80
- const finalSkillsArray = overrideVersionsFromConfig(updatedSkillsArray, config);
84
+ const finalSkillsArray = config ? overrideVersionsFromConfig(updatedSkillsArray, config) : updatedSkillsArray;
81
85
  // Write deployment data
82
86
  const deployData = {
83
87
  skills: finalSkillsArray
@@ -169,6 +173,10 @@ function buildSkillsArray(skillsMetadata, skillToTools, tools) {
169
173
  * @returns Skills array with versions overridden from config
170
174
  */
171
175
  function overrideVersionsFromConfig(skillsArray, config) {
176
+ // Handle null config
177
+ if (!config) {
178
+ return skillsArray;
179
+ }
172
180
  // Get version map from YAML config
173
181
  const configVersionMap = new Map();
174
182
  if (config.skills && Array.isArray(config.skills)) {
@@ -22,5 +22,6 @@ export declare function preBundleJobsInSource(sourceFilePath: string, project: P
22
22
  /**
23
23
  * Replaces placeholders in bundled code with actual job bundles
24
24
  * The bundled job code is NOT compressed - it's JavaScript that will be part of the tool
25
+ * Validates that all placeholders are found and replaced
25
26
  */
26
27
  export declare function replaceJobPlaceholders(bundledCode: string, jobBundles: Map<string, string>): string;
@@ -162,15 +162,27 @@ export default executeFunc;
162
162
  /**
163
163
  * Replaces placeholders in bundled code with actual job bundles
164
164
  * The bundled job code is NOT compressed - it's JavaScript that will be part of the tool
165
+ * Validates that all placeholders are found and replaced
165
166
  */
166
167
  export function replaceJobPlaceholders(bundledCode, jobBundles) {
167
168
  let result = bundledCode;
169
+ const replacementsMade = new Map();
168
170
  jobBundles.forEach((bundledJobCode, placeholder) => {
169
171
  // The placeholder is used as an identifier (not a string), just replace it directly
170
172
  // It appears as: execute: __BUNDLED_JOB_EXECUTE___1
171
- const placeholderPattern = new RegExp(placeholder, 'g');
173
+ // Use word boundary to prevent partial matches
174
+ const placeholderPattern = new RegExp(`\\b${placeholder}\\b`, 'g');
175
+ const beforeLength = result.length;
172
176
  result = result.replace(placeholderPattern, bundledJobCode);
173
- // console.log(`[PreBundleJobs] Replaced ${placeholder} with bundled code (${(bundledJobCode.length / 1024).toFixed(1)}KB)`);
177
+ const wasReplaced = result.length !== beforeLength;
178
+ replacementsMade.set(placeholder, wasReplaced);
179
+ if (wasReplaced) {
180
+ // console.log(`[PreBundleJobs] ✅ Replaced ${placeholder} with bundled code (${(bundledJobCode.length / 1024).toFixed(1)}KB)`);
181
+ }
182
+ else {
183
+ console.warn(`⚠️ Warning: Placeholder ${placeholder} not found in bundled code. Nested job may not work correctly.`);
184
+ console.warn(` This can happen if esbuild mangled the identifier during minification.`);
185
+ }
174
186
  });
175
187
  return result;
176
188
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lua-cli",
3
- "version": "3.0.2-alpha.2",
3
+ "version": "3.0.2-alpha.4",
4
4
  "description": "Build, test, and deploy AI agents with custom tools, webhooks, and scheduled jobs. Features LuaAgent unified configuration, streaming chat, and batch deployment.",
5
5
  "readmeFilename": "README.md",
6
6
  "main": "dist/api-exports.js",
@@ -0,0 +1 @@
1
+ skills: []
@@ -25,7 +25,7 @@ export class GetAllProductsTool implements LuaTool {
25
25
  });
26
26
 
27
27
  async execute(input: z.infer<typeof this.inputSchema>) {
28
- return await Products.get(input.limit, input.limit);
28
+ return await Products.get(input.page, input.limit);
29
29
  }
30
30
  }
31
31