lua-cli 1.1.2 → 1.1.4-beta.0

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.
@@ -0,0 +1 @@
1
+ export declare function agentsCommand(): Promise<void>;
@@ -0,0 +1 @@
1
+ export declare function apiKeyCommand(): Promise<void>;
@@ -0,0 +1 @@
1
+ export declare function configureCommand(): Promise<void>;
@@ -0,0 +1 @@
1
+ export declare function deployCommand(): Promise<void>;
@@ -0,0 +1,720 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ export async function deployCommand() {
4
+ try {
5
+ console.log("🔨 Compiling Lua skill...");
6
+ // Read package.json to get version and name
7
+ const packageJsonPath = path.join(process.cwd(), "package.json");
8
+ if (!fs.existsSync(packageJsonPath)) {
9
+ throw new Error("package.json not found in current directory");
10
+ }
11
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
12
+ const version = packageJson.version || "1.0.0";
13
+ const skillsName = packageJson.name || "lua-skill";
14
+ // Read index.ts file
15
+ const indexPath = path.join(process.cwd(), "index.ts");
16
+ if (!fs.existsSync(indexPath)) {
17
+ throw new Error("index.ts not found in current directory");
18
+ }
19
+ const indexContent = fs.readFileSync(indexPath, "utf8");
20
+ // Extract skill information
21
+ const skillInfo = await extractSkillInfo(indexContent);
22
+ // Create deployment data
23
+ const deployData = {
24
+ version,
25
+ skillsName,
26
+ tools: skillInfo
27
+ };
28
+ // Create .lua directory
29
+ const luaDir = path.join(process.cwd(), ".lua");
30
+ if (!fs.existsSync(luaDir)) {
31
+ fs.mkdirSync(luaDir, { recursive: true });
32
+ }
33
+ // Write JSON output to .lua directory
34
+ const jsonOutputPath = path.join(luaDir, "deploy.json");
35
+ fs.writeFileSync(jsonOutputPath, JSON.stringify(deployData, null, 2));
36
+ // Write individual tool files to .lua directory
37
+ for (const tool of skillInfo) {
38
+ const toolFilePath = path.join(luaDir, `${tool.name}.js`);
39
+ fs.writeFileSync(toolFilePath, tool.execute);
40
+ }
41
+ console.log(`📁 Compiled files written to: ${luaDir}`);
42
+ console.log(`📄 JSON output: ${jsonOutputPath}`);
43
+ console.log(`🔧 Tool files: ${skillInfo.map(t => `${t.name}.js`).join(', ')}`);
44
+ console.log("✅ Skill compilation completed successfully!");
45
+ }
46
+ catch (error) {
47
+ console.error("❌ Compilation failed:", error.message);
48
+ process.exit(1);
49
+ }
50
+ }
51
+ async function extractSkillInfo(indexContent) {
52
+ const tools = [];
53
+ // Find inline addTool calls: skill.addTool({...})
54
+ const inlineAddToolRegex = /skill\.addTool\(\s*\{([\s\S]*?)\}\s*\)/g;
55
+ let match;
56
+ while ((match = inlineAddToolRegex.exec(indexContent)) !== null) {
57
+ const toolContent = match[1];
58
+ // Extract tool properties
59
+ const nameMatch = toolContent.match(/name:\s*["']([^"']+)["']/);
60
+ const descriptionMatch = toolContent.match(/description:\s*["']([^"']+)["']/);
61
+ const inputSchemaMatch = toolContent.match(/inputSchema:\s*(\w+)/);
62
+ const outputSchemaMatch = toolContent.match(/outputSchema:\s*(\w+)/);
63
+ const executeMatch = toolContent.match(/execute:\s*async\s*\([^)]*\)\s*=>\s*\{([\s\S]*?)\}/);
64
+ if (nameMatch && descriptionMatch && inputSchemaMatch && outputSchemaMatch && executeMatch) {
65
+ const toolName = nameMatch[1];
66
+ const toolDescription = descriptionMatch[1];
67
+ const inputSchemaVar = inputSchemaMatch[1];
68
+ const outputSchemaVar = outputSchemaMatch[1];
69
+ const executeBody = executeMatch[1];
70
+ // Convert schemas to JSON Schema format
71
+ const inputSchema = convertSchemaToJSON(inputSchemaVar, indexContent);
72
+ const outputSchema = convertSchemaToJSON(outputSchemaVar, indexContent);
73
+ // Create self-contained execute function
74
+ const selfContainedExecute = await createSelfContainedExecute(executeBody, indexContent);
75
+ tools.push({
76
+ name: toolName,
77
+ description: toolDescription,
78
+ inputSchema,
79
+ outputSchema,
80
+ execute: selfContainedExecute
81
+ });
82
+ }
83
+ }
84
+ // Find class-based addTool calls: skill.addTool(new SomeTool())
85
+ const classAddToolRegex = /skill\.addTool\(\s*new\s+(\w+)\(\)\s*\)/g;
86
+ let classMatch;
87
+ while ((classMatch = classAddToolRegex.exec(indexContent)) !== null) {
88
+ const className = classMatch[1];
89
+ // Find the tool class definition
90
+ const toolInfo = await extractToolFromClass(className, indexContent);
91
+ if (toolInfo) {
92
+ tools.push(toolInfo);
93
+ }
94
+ }
95
+ return tools;
96
+ }
97
+ function convertSchemaToJSON(schemaVar, indexContent) {
98
+ // Find the schema definition
99
+ const schemaRegex = new RegExp(`const\\s+${schemaVar}\\s*=\\s*z\\.object\\(\\{([\\s\\S]*?)\\}\\\);`, 'g');
100
+ const match = schemaRegex.exec(indexContent);
101
+ if (match) {
102
+ const schemaContent = match[1];
103
+ // Convert Zod schema to JSON Schema format
104
+ return {
105
+ type: "object",
106
+ properties: parseZodProperties(schemaContent),
107
+ required: extractRequiredFields(schemaContent)
108
+ };
109
+ }
110
+ // If no match found, return empty schema
111
+ return { type: "object", properties: {} };
112
+ }
113
+ function parseZodProperties(schemaContent) {
114
+ const properties = {};
115
+ // Simple regex to find z.string(), z.number(), etc.
116
+ const fieldRegex = /(\w+):\s*z\.(\w+)\(\)/g;
117
+ let match;
118
+ while ((match = fieldRegex.exec(schemaContent)) !== null) {
119
+ const fieldName = match[1];
120
+ const fieldType = match[2];
121
+ switch (fieldType) {
122
+ case 'string':
123
+ properties[fieldName] = { type: 'string' };
124
+ break;
125
+ case 'number':
126
+ properties[fieldName] = { type: 'number' };
127
+ break;
128
+ case 'boolean':
129
+ properties[fieldName] = { type: 'boolean' };
130
+ break;
131
+ default:
132
+ properties[fieldName] = { type: 'string' };
133
+ }
134
+ }
135
+ return properties;
136
+ }
137
+ function extractRequiredFields(schemaContent) {
138
+ const required = [];
139
+ const fieldRegex = /(\w+):\s*z\.(\w+)\(\)/g;
140
+ let match;
141
+ while ((match = fieldRegex.exec(schemaContent)) !== null) {
142
+ required.push(match[1]);
143
+ }
144
+ return required;
145
+ }
146
+ async function createSelfContainedExecute(executeBody, indexContent) {
147
+ const dependencies = [];
148
+ const bundledPackages = new Set();
149
+ // 1. Parse external package imports and bundle their code
150
+ const allImportRegex = /import\s+(?:(?:\{([^}]+)\})|(\w+))\s+from\s+["']([^"']+)["']/g;
151
+ let importMatch;
152
+ while ((importMatch = allImportRegex.exec(indexContent)) !== null) {
153
+ const namedImports = importMatch[1]; // Named imports like { z }
154
+ const defaultImport = importMatch[2]; // Default import like axios
155
+ const packagePath = importMatch[3];
156
+ // Skip local imports (relative paths)
157
+ if (packagePath.startsWith('./') || packagePath.startsWith('../')) {
158
+ continue;
159
+ }
160
+ // Skip lua-cli imports (these are handled separately)
161
+ if (packagePath.startsWith('lua-cli')) {
162
+ continue;
163
+ }
164
+ // Skip zod - assume it's always available on target machine
165
+ if (packagePath === 'zod') {
166
+ // Add require statement for zod instead of bundling
167
+ if (namedImports) {
168
+ const importsList = namedImports.split(',').map(imp => imp.trim());
169
+ const usedImports = importsList.filter(imp => executeBody.includes(imp) || indexContent.includes(`${imp}.`));
170
+ if (usedImports.length > 0) {
171
+ const requireStatement = usedImports.length === 1
172
+ ? `const { ${usedImports[0]} } = require('zod');`
173
+ : `const { ${usedImports.join(', ')} } = require('zod');`;
174
+ bundledPackages.add(requireStatement);
175
+ }
176
+ }
177
+ else if (defaultImport) {
178
+ bundledPackages.add(`const ${defaultImport} = require('zod');`);
179
+ }
180
+ continue;
181
+ }
182
+ // Bundle other external packages
183
+ if (namedImports || defaultImport) {
184
+ const packageCode = await bundlePackageCode(packagePath, namedImports, defaultImport);
185
+ if (packageCode) {
186
+ bundledPackages.add(packageCode);
187
+ }
188
+ }
189
+ }
190
+ // 2. Extract class definitions with proper brace matching
191
+ const classRegex = /class\s+(\w+)(?:\s+extends\s+\w+)?\s*\{/g;
192
+ let classMatch;
193
+ while ((classMatch = classRegex.exec(indexContent)) !== null) {
194
+ const className = classMatch[1];
195
+ const classStart = classMatch.index;
196
+ // Find the matching closing brace
197
+ let braceCount = 0;
198
+ let classEnd = classStart;
199
+ let found = false;
200
+ for (let i = classStart; i < indexContent.length; i++) {
201
+ if (indexContent[i] === '{') {
202
+ braceCount++;
203
+ }
204
+ else if (indexContent[i] === '}') {
205
+ braceCount--;
206
+ if (braceCount === 0) {
207
+ classEnd = i;
208
+ found = true;
209
+ break;
210
+ }
211
+ }
212
+ }
213
+ if (found) {
214
+ const fullClass = indexContent.substring(classStart, classEnd + 1);
215
+ // Check if this class is used in the execute function
216
+ let isUsed = false;
217
+ // Direct usage in execute body
218
+ if (executeBody.includes(`new ${className}`) ||
219
+ executeBody.includes(`${className}.`) ||
220
+ executeBody.includes(`${className}(`)) {
221
+ isUsed = true;
222
+ }
223
+ // Check if any variable that uses this class is referenced in execute body
224
+ const variableRegex = new RegExp(`(?:const|let|var)\\s+(\\w+)\\s*=\\s*new\\s+${className}\\s*\\([^)]*\\);`, 'g');
225
+ let varMatch;
226
+ while ((varMatch = variableRegex.exec(indexContent)) !== null) {
227
+ const varName = varMatch[1];
228
+ if (executeBody.includes(varName)) {
229
+ isUsed = true;
230
+ break;
231
+ }
232
+ }
233
+ if (isUsed) {
234
+ dependencies.push(fullClass);
235
+ }
236
+ }
237
+ }
238
+ // 3. Extract function definitions
239
+ const functionRegex = /(?:async\s+)?function\s+(\w+)\s*\([^)]*\)\s*\{([\s\S]*?)\n\}/g;
240
+ let functionMatch;
241
+ while ((functionMatch = functionRegex.exec(indexContent)) !== null) {
242
+ const functionName = functionMatch[1];
243
+ const functionBody = functionMatch[2];
244
+ if (executeBody.includes(functionName)) {
245
+ dependencies.push(`function ${functionName}() {\n${functionBody}\n}`);
246
+ }
247
+ }
248
+ // 4. Extract const/let/var declarations (avoid duplicates)
249
+ const varRegex = /(?:const|let|var)\s+(\w+)\s*=\s*([^;]+);/g;
250
+ const declaredVars = new Set();
251
+ let varMatch;
252
+ while ((varMatch = varRegex.exec(indexContent)) !== null) {
253
+ const varName = varMatch[1];
254
+ const varValue = varMatch[2];
255
+ // Skip if it's a class instantiation (we'll handle that separately)
256
+ if (varValue.includes('new ') && varValue.includes('()')) {
257
+ continue;
258
+ }
259
+ // Skip if already declared
260
+ if (declaredVars.has(varName)) {
261
+ continue;
262
+ }
263
+ if (executeBody.includes(varName)) {
264
+ declaredVars.add(varName);
265
+ dependencies.push(`const ${varName} = ${varValue};`);
266
+ }
267
+ }
268
+ // 5. Extract class instantiations (avoid duplicates)
269
+ const instantiationRegex = /(?:const|let|var)\s+(\w+)\s*=\s*new\s+(\w+)\([^)]*\);/g;
270
+ let instantiationMatch;
271
+ while ((instantiationMatch = instantiationRegex.exec(indexContent)) !== null) {
272
+ const instanceName = instantiationMatch[1];
273
+ const className = instantiationMatch[2];
274
+ // Skip if already declared
275
+ if (declaredVars.has(instanceName)) {
276
+ continue;
277
+ }
278
+ if (executeBody.includes(instanceName)) {
279
+ declaredVars.add(instanceName);
280
+ dependencies.push(`const ${instanceName} = new ${className}();`);
281
+ }
282
+ }
283
+ // 6. Create the self-contained execute function
284
+ const allDependencies = [...Array.from(bundledPackages), ...dependencies];
285
+ const dependencyCode = allDependencies.join('\n');
286
+ // Strip TypeScript type annotations for JavaScript compatibility (only for local code)
287
+ const cleanDependencyCode = allDependencies.map(dep => {
288
+ // Only strip TypeScript from local dependencies, not bundled packages
289
+ if (dep.includes('require(') || dep.includes('import ')) {
290
+ return dep; // Skip bundled packages
291
+ }
292
+ return dep
293
+ .replace(/:\s*string/g, '') // Remove : string
294
+ .replace(/:\s*number/g, '') // Remove : number
295
+ .replace(/:\s*boolean/g, '') // Remove : boolean
296
+ .replace(/:\s*any/g, '') // Remove : any
297
+ .replace(/:\s*void/g, '') // Remove : void
298
+ .replace(/:\s*object/g, '') // Remove : object
299
+ .replace(/:\s*Array<[^>]+>/g, '') // Remove : Array<Type>
300
+ .replace(/:\s*Promise<[^>]+>/g, '') // Remove : Promise<Type>
301
+ .replace(/:\s*Record<[^>]+>/g, ''); // Remove : Record<Type>
302
+ }).join('\n');
303
+ const cleanExecuteBody = executeBody
304
+ .replace(/:\s*string/g, '') // Remove : string
305
+ .replace(/:\s*number/g, '') // Remove : number
306
+ .replace(/:\s*boolean/g, '') // Remove : boolean
307
+ .replace(/:\s*any/g, '') // Remove : any
308
+ .replace(/:\s*void/g, '') // Remove : void
309
+ .replace(/:\s*object/g, '') // Remove : object
310
+ .replace(/:\s*Array<[^>]+>/g, '') // Remove : Array<Type>
311
+ .replace(/:\s*Promise<[^>]+>/g, '') // Remove : Promise<Type>
312
+ .replace(/:\s*Record<[^>]+>/g, ''); // Remove : Record<Type>
313
+ const selfContainedExecute = `async (input) => {
314
+ ${cleanDependencyCode ? ` ${cleanDependencyCode.split('\n').join('\n ')}\n` : ''} ${cleanExecuteBody.trim()}
315
+ }`;
316
+ return selfContainedExecute;
317
+ }
318
+ async function bundlePackageCode(packagePath, namedImports, defaultImport) {
319
+ try {
320
+ const { build } = await import('esbuild');
321
+ // Create a temporary entry file for esbuild in .lua directory
322
+ const luaDir = path.join(process.cwd(), '.lua');
323
+ if (!fs.existsSync(luaDir)) {
324
+ fs.mkdirSync(luaDir, { recursive: true });
325
+ }
326
+ const entryFile = path.join(luaDir, `${packagePath}-entry.js`);
327
+ const outputFile = path.join(luaDir, `${packagePath}-bundle.js`);
328
+ // Create entry file based on import type
329
+ let entryContent = '';
330
+ if (defaultImport) {
331
+ // For default imports like `import axios from 'axios'`
332
+ entryContent = `import ${defaultImport} from '${packagePath}';\nmodule.exports = ${defaultImport};`;
333
+ }
334
+ else if (namedImports) {
335
+ // For named imports like `import { z } from 'zod'`
336
+ const importsList = namedImports.split(',').map(imp => imp.trim());
337
+ entryContent = `import { ${importsList.join(', ')} } from '${packagePath}';\nmodule.exports = { ${importsList.join(', ')} };`;
338
+ }
339
+ else {
340
+ // Fallback - import everything
341
+ entryContent = `import * as ${packagePath.replace(/[^a-zA-Z0-9]/g, '_')} from '${packagePath}';\nmodule.exports = ${packagePath.replace(/[^a-zA-Z0-9]/g, '_')};`;
342
+ }
343
+ // Write entry file
344
+ fs.writeFileSync(entryFile, entryContent);
345
+ // Bundle with esbuild
346
+ const result = await build({
347
+ entryPoints: [entryFile],
348
+ bundle: true,
349
+ format: 'cjs', // CommonJS format
350
+ platform: 'node',
351
+ target: 'node16',
352
+ outfile: outputFile,
353
+ external: [], // Bundle everything
354
+ minify: false, // Keep readable for debugging
355
+ sourcemap: false,
356
+ write: true,
357
+ resolveExtensions: ['.js', '.ts', '.json'],
358
+ mainFields: ['main', 'module', 'browser'],
359
+ conditions: ['node'],
360
+ nodePaths: [
361
+ path.join(process.cwd(), 'node_modules'),
362
+ path.join(process.cwd(), '..', 'node_modules'),
363
+ path.join(process.cwd(), '..', '..', 'node_modules')
364
+ ],
365
+ absWorkingDir: process.cwd(),
366
+ });
367
+ if (result.errors.length > 0) {
368
+ console.warn(`Warning: esbuild errors for package ${packagePath}:`, result.errors);
369
+ return null;
370
+ }
371
+ // Read the bundled output
372
+ if (!fs.existsSync(outputFile)) {
373
+ console.warn(`Warning: Bundle output not found for package ${packagePath}`);
374
+ return null;
375
+ }
376
+ const bundledContent = fs.readFileSync(outputFile, 'utf8');
377
+ // Clean up temporary files
378
+ try {
379
+ fs.unlinkSync(entryFile);
380
+ fs.unlinkSync(outputFile);
381
+ }
382
+ catch (cleanupError) {
383
+ // Ignore cleanup errors
384
+ }
385
+ // Create the final bundled code
386
+ let finalCode = '';
387
+ if (defaultImport) {
388
+ finalCode = `const ${defaultImport} = (function() {\n${bundledContent}\n return module.exports;\n})();\n`;
389
+ }
390
+ else if (namedImports) {
391
+ const importsList = namedImports.split(',').map(imp => imp.trim());
392
+ finalCode = `(function() {\n${bundledContent}\n})();\n`;
393
+ finalCode += `const { ${importsList.join(', ')} } = module.exports;\n`;
394
+ }
395
+ else {
396
+ finalCode = `(function() {\n${bundledContent}\n})();\n`;
397
+ }
398
+ return finalCode;
399
+ }
400
+ catch (error) {
401
+ console.warn(`Warning: Could not bundle package ${packagePath} with esbuild:`, error);
402
+ // For test environments or when esbuild fails, provide a fallback
403
+ if (packagePath === 'axios') {
404
+ return createWorkingAxiosImplementation();
405
+ }
406
+ return null;
407
+ }
408
+ }
409
+ function createWorkingAxiosImplementation() {
410
+ return `
411
+ // Working axios implementation using native fetch (for test environments)
412
+ const axios = {
413
+ get: async (url, config = {}) => {
414
+ const searchParams = new URLSearchParams(config.params || {});
415
+ const fullUrl = searchParams.toString() ? \`\${url}?\${searchParams}\` : url;
416
+
417
+ const response = await fetch(fullUrl, {
418
+ method: 'GET',
419
+ headers: {
420
+ 'Content-Type': 'application/json',
421
+ ...config.headers
422
+ }
423
+ });
424
+
425
+ if (!response.ok) {
426
+ const error = new Error(\`Request failed with status \${response.status}\`);
427
+ error.response = { status: response.status, statusText: response.statusText };
428
+ throw error;
429
+ }
430
+
431
+ const data = await response.json();
432
+ return {
433
+ data,
434
+ status: response.status,
435
+ statusText: response.statusText,
436
+ headers: response.headers,
437
+ config: config
438
+ };
439
+ },
440
+
441
+ post: async (url, data, config = {}) => {
442
+ const response = await fetch(url, {
443
+ method: 'POST',
444
+ headers: {
445
+ 'Content-Type': 'application/json',
446
+ ...config.headers
447
+ },
448
+ body: JSON.stringify(data)
449
+ });
450
+
451
+ if (!response.ok) {
452
+ const error = new Error(\`Request failed with status \${response.status}\`);
453
+ error.response = { status: response.status, statusText: response.statusText };
454
+ throw error;
455
+ }
456
+
457
+ const responseData = await response.json();
458
+ return {
459
+ data: responseData,
460
+ status: response.status,
461
+ statusText: response.statusText,
462
+ headers: response.headers,
463
+ config: config
464
+ };
465
+ },
466
+
467
+ put: async (url, data, config = {}) => {
468
+ const response = await fetch(url, {
469
+ method: 'PUT',
470
+ headers: {
471
+ 'Content-Type': 'application/json',
472
+ ...config.headers
473
+ },
474
+ body: JSON.stringify(data)
475
+ });
476
+
477
+ if (!response.ok) {
478
+ const error = new Error(\`Request failed with status \${response.status}\`);
479
+ error.response = { status: response.status, statusText: response.statusText };
480
+ throw error;
481
+ }
482
+
483
+ const responseData = await response.json();
484
+ return {
485
+ data: responseData,
486
+ status: response.status,
487
+ statusText: response.statusText,
488
+ headers: response.headers,
489
+ config: config
490
+ };
491
+ },
492
+
493
+ delete: async (url, config = {}) => {
494
+ const response = await fetch(url, {
495
+ method: 'DELETE',
496
+ headers: {
497
+ 'Content-Type': 'application/json',
498
+ ...config.headers
499
+ }
500
+ });
501
+
502
+ if (!response.ok) {
503
+ const error = new Error(\`Request failed with status \${response.status}\`);
504
+ error.response = { status: response.status, statusText: response.statusText };
505
+ throw error;
506
+ }
507
+
508
+ const responseData = await response.json();
509
+ return {
510
+ data: responseData,
511
+ status: response.status,
512
+ statusText: response.statusText,
513
+ headers: response.headers,
514
+ config: config
515
+ };
516
+ },
517
+
518
+ patch: async (url, data, config = {}) => {
519
+ const response = await fetch(url, {
520
+ method: 'PATCH',
521
+ headers: {
522
+ 'Content-Type': 'application/json',
523
+ ...config.headers
524
+ },
525
+ body: JSON.stringify(data)
526
+ });
527
+
528
+ if (!response.ok) {
529
+ const error = new Error(\`Request failed with status \${response.status}\`);
530
+ error.response = { status: response.status, statusText: response.statusText };
531
+ throw error;
532
+ }
533
+
534
+ const responseData = await response.json();
535
+ return {
536
+ data: responseData,
537
+ status: response.status,
538
+ statusText: response.statusText,
539
+ headers: response.headers,
540
+ config: config
541
+ };
542
+ }
543
+ };
544
+ `;
545
+ }
546
+ async function extractToolFromClass(className, indexContent) {
547
+ // Find the import statement for this class
548
+ const importRegex = new RegExp(`import\\s+${className}\\s+from\\s+["']([^"']+)["']`, 'g');
549
+ const importMatch = importRegex.exec(indexContent);
550
+ if (!importMatch) {
551
+ console.warn(`Warning: Could not find import for class ${className}`);
552
+ return null;
553
+ }
554
+ const importPath = importMatch[1];
555
+ // Read the tool file
556
+ const toolFilePath = path.join(process.cwd(), importPath.replace('./', '') + '.ts');
557
+ if (!fs.existsSync(toolFilePath)) {
558
+ console.warn(`Warning: Tool file not found: ${toolFilePath}`);
559
+ return null;
560
+ }
561
+ const toolContent = fs.readFileSync(toolFilePath, 'utf8');
562
+ // Extract tool properties from the class
563
+ const nameMatch = toolContent.match(/this\.name\s*=\s*["']([^"']+)["']/);
564
+ const descriptionMatch = toolContent.match(/this\.description\s*=\s*["']([^"']+)["']/);
565
+ if (!nameMatch || !descriptionMatch) {
566
+ console.warn(`Warning: Could not extract name or description from ${className}`);
567
+ return null;
568
+ }
569
+ const toolName = nameMatch[1];
570
+ const toolDescription = descriptionMatch[1];
571
+ // Extract schemas
572
+ const inputSchemaMatch = toolContent.match(/const\s+(\w+)\s*=\s*z\.object\(/);
573
+ const outputSchemaMatch = toolContent.match(/const\s+(\w+)\s*=\s*z\.object\(/);
574
+ if (!inputSchemaMatch) {
575
+ console.warn(`Warning: Could not find input schema in ${className}`);
576
+ return null;
577
+ }
578
+ // Convert schemas to JSON Schema format
579
+ const inputSchema = convertSchemaToJSON(inputSchemaMatch[1], toolContent);
580
+ const outputSchema = outputSchemaMatch ? convertSchemaToJSON(outputSchemaMatch[1], toolContent) : { type: "object" };
581
+ // Extract execute method
582
+ const executeMatch = toolContent.match(/async\s+execute\s*\([^)]*\)\s*\{([\s\S]*?)\}/);
583
+ if (!executeMatch) {
584
+ console.warn(`Warning: Could not find execute method in ${className}`);
585
+ return null;
586
+ }
587
+ const executeBody = executeMatch[1];
588
+ // For class-based tools, we need to create a self-contained function that includes:
589
+ // 1. The service classes
590
+ // 2. Class instantiation
591
+ // 3. The execute logic
592
+ const selfContainedExecute = await createClassBasedExecute(executeBody, toolContent, className);
593
+ return {
594
+ name: toolName,
595
+ description: toolDescription,
596
+ inputSchema,
597
+ outputSchema,
598
+ execute: selfContainedExecute
599
+ };
600
+ }
601
+ async function createClassBasedExecute(executeBody, toolContent, className) {
602
+ const dependencies = [];
603
+ const bundledPackages = new Set();
604
+ // 1. Parse imports from the tool file
605
+ const importRegex = /import\s+(?:(?:\{([^}]+)\})|(\w+))\s+from\s+["']([^"']+)["']/g;
606
+ let importMatch;
607
+ while ((importMatch = importRegex.exec(toolContent)) !== null) {
608
+ const namedImports = importMatch[1];
609
+ const defaultImport = importMatch[2];
610
+ const packagePath = importMatch[3];
611
+ // Skip lua-cli imports
612
+ if (packagePath.startsWith('lua-cli')) {
613
+ continue;
614
+ }
615
+ // Handle zod
616
+ if (packagePath === 'zod') {
617
+ if (namedImports) {
618
+ const importsList = namedImports.split(',').map(imp => imp.trim());
619
+ const usedImports = importsList.filter(imp => executeBody.includes(imp) || toolContent.includes(`${imp}.`));
620
+ if (usedImports.length > 0) {
621
+ const requireStatement = usedImports.length === 1
622
+ ? `const { ${usedImports[0]} } = require('zod');`
623
+ : `const { ${usedImports.join(', ')} } = require('zod');`;
624
+ bundledPackages.add(requireStatement);
625
+ }
626
+ }
627
+ else if (defaultImport) {
628
+ bundledPackages.add(`const ${defaultImport} = require('zod');`);
629
+ }
630
+ continue;
631
+ }
632
+ // Handle axios
633
+ if (packagePath === 'axios') {
634
+ bundledPackages.add(`const axios = require('axios');`);
635
+ continue;
636
+ }
637
+ // Handle local service imports
638
+ if (packagePath.startsWith('./') || packagePath.startsWith('../')) {
639
+ // The tool files are in tools/ subdirectory, so we need to resolve from there
640
+ const toolDir = path.join(process.cwd(), 'tools');
641
+ const serviceFilePath = path.resolve(toolDir, packagePath + '.ts');
642
+ if (fs.existsSync(serviceFilePath)) {
643
+ const serviceContent = fs.readFileSync(serviceFilePath, 'utf8');
644
+ // Check for axios import in service file
645
+ if (serviceContent.includes("import axios from \"axios\"")) {
646
+ bundledPackages.add(`const axios = require('axios');`);
647
+ }
648
+ // Extract the service class with proper brace matching
649
+ const classRegex = /class\s+(\w+)(?:\s+extends\s+\w+)?\s*\{/g;
650
+ let classMatch = classRegex.exec(serviceContent);
651
+ if (classMatch) {
652
+ const serviceClassName = classMatch[1];
653
+ const startIndex = classMatch.index + classMatch[0].length - 1; // Position of opening brace
654
+ // Find matching closing brace
655
+ let braceCount = 1;
656
+ let endIndex = startIndex + 1;
657
+ while (endIndex < serviceContent.length && braceCount > 0) {
658
+ if (serviceContent[endIndex] === '{')
659
+ braceCount++;
660
+ else if (serviceContent[endIndex] === '}')
661
+ braceCount--;
662
+ endIndex++;
663
+ }
664
+ if (braceCount === 0) {
665
+ const serviceClassBody = serviceContent.substring(startIndex + 1, endIndex - 1);
666
+ // Clean up the class body (remove TypeScript types)
667
+ const cleanClassBody = serviceClassBody
668
+ .replace(/:\s*string/g, '')
669
+ .replace(/:\s*number/g, '')
670
+ .replace(/:\s*boolean/g, '')
671
+ .replace(/:\s*any/g, '')
672
+ .replace(/:\s*void/g, '')
673
+ .replace(/:\s*Promise<[^>]+>/g, '')
674
+ .replace(/:\s*Record<[^>]+>/g, '');
675
+ // Create the service class
676
+ const serviceClass = `class ${serviceClassName} {\n${cleanClassBody}\n}`;
677
+ dependencies.push(serviceClass);
678
+ }
679
+ }
680
+ }
681
+ continue;
682
+ }
683
+ // Bundle other external packages
684
+ if (namedImports || defaultImport) {
685
+ const packageCode = await bundlePackageCode(packagePath, namedImports, defaultImport);
686
+ if (packageCode) {
687
+ bundledPackages.add(packageCode);
688
+ }
689
+ }
690
+ }
691
+ // 2. Extract class instantiation from constructor
692
+ const constructorMatch = toolContent.match(/constructor\s*\([^)]*\)\s*\{([\s\S]*?)\}/);
693
+ if (constructorMatch) {
694
+ const constructorBody = constructorMatch[1];
695
+ // Extract service instantiation
696
+ const serviceInstantiationMatch = constructorBody.match(/this\.(\w+)\s*=\s*new\s+(\w+)\([^)]*\);/);
697
+ if (serviceInstantiationMatch) {
698
+ const serviceProperty = serviceInstantiationMatch[1];
699
+ const serviceClass = serviceInstantiationMatch[2];
700
+ dependencies.push(`const ${serviceProperty} = new ${serviceClass}();`);
701
+ }
702
+ }
703
+ // 3. Create the self-contained execute function
704
+ const allDependencies = [...Array.from(bundledPackages), ...dependencies];
705
+ const dependencyCode = allDependencies.join('\n');
706
+ // Clean the execute body (remove TypeScript types)
707
+ const cleanExecuteBody = executeBody
708
+ .replace(/:\s*string/g, '')
709
+ .replace(/:\s*number/g, '')
710
+ .replace(/:\s*boolean/g, '')
711
+ .replace(/:\s*any/g, '')
712
+ .replace(/:\s*void/g, '')
713
+ .replace(/:\s*Promise<[^>]+>/g, '')
714
+ .replace(/:\s*Record<[^>]+>/g, '');
715
+ // Replace this.serviceProperty with the instantiated service
716
+ const finalExecuteBody = cleanExecuteBody.replace(/this\.(\w+)/g, '$1');
717
+ return `async (input) => {
718
+ ${dependencyCode ? dependencyCode + '\n' : ''}${finalExecuteBody}
719
+ }`;
720
+ }
@@ -0,0 +1 @@
1
+ export declare function destroyCommand(): Promise<void>;
@@ -0,0 +1,7 @@
1
+ export { configureCommand } from "./configure.js";
2
+ export { initCommand } from "./init.js";
3
+ export { destroyCommand } from "./destroy.js";
4
+ export { apiKeyCommand } from "./apiKey.js";
5
+ export { agentsCommand } from "./agents.js";
6
+ export { deployCommand } from "./deploy.js";
7
+ export { testCommand } from "./test.js";
@@ -3,3 +3,5 @@ export { initCommand } from "./init.js";
3
3
  export { destroyCommand } from "./destroy.js";
4
4
  export { apiKeyCommand } from "./apiKey.js";
5
5
  export { agentsCommand } from "./agents.js";
6
+ export { deployCommand } from "./deploy.js";
7
+ export { testCommand } from "./test.js";
@@ -0,0 +1 @@
1
+ export declare function initCommand(): Promise<void>;
@@ -66,4 +66,5 @@ export async function initCommand() {
66
66
  copyTemplateFiles(templateDir, currentDir);
67
67
  console.log("✅ Copied template files");
68
68
  console.log("🎉 Lua skill project initialized successfully!");
69
+ console.log("📦 Run `npm install` to install dependencies");
69
70
  }
@@ -0,0 +1 @@
1
+ export declare function testCommand(): Promise<void>;
@@ -0,0 +1,141 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import inquirer from "inquirer";
4
+ import { deployCommand } from "./deploy.js";
5
+ export async function testCommand() {
6
+ try {
7
+ console.log("🧪 Testing Lua skill...");
8
+ // First, compile the code
9
+ console.log("📦 Compiling code first...");
10
+ await deployCommand();
11
+ // Check if .lua directory exists
12
+ const luaDir = path.join(process.cwd(), ".lua");
13
+ if (!fs.existsSync(luaDir)) {
14
+ throw new Error(".lua directory not found. Run 'lua compile' first.");
15
+ }
16
+ // Read deploy.json
17
+ const deployJsonPath = path.join(luaDir, "deploy.json");
18
+ if (!fs.existsSync(deployJsonPath)) {
19
+ throw new Error("deploy.json not found. Run 'lua compile' first.");
20
+ }
21
+ const deployData = JSON.parse(fs.readFileSync(deployJsonPath, "utf8"));
22
+ if (!deployData.tools || deployData.tools.length === 0) {
23
+ throw new Error("No tools found in deploy.json");
24
+ }
25
+ // Let user select a tool using picker
26
+ const toolChoices = deployData.tools.map((tool) => ({
27
+ name: `${tool.name} - ${tool.description}`,
28
+ value: tool
29
+ }));
30
+ const { selectedTool } = await inquirer.prompt([
31
+ {
32
+ type: 'list',
33
+ name: 'selectedTool',
34
+ message: '🔧 Select a tool to test:',
35
+ choices: toolChoices,
36
+ pageSize: 10
37
+ }
38
+ ]);
39
+ console.log(`\n✅ Selected tool: ${selectedTool.name}`);
40
+ // Get input values for each required parameter using inquirer
41
+ const inputValues = {};
42
+ const inputSchema = selectedTool.inputSchema;
43
+ if (inputSchema.properties) {
44
+ console.log("\n📝 Enter input values:");
45
+ const inputPrompts = [];
46
+ for (const [key, value] of Object.entries(inputSchema.properties)) {
47
+ const property = value;
48
+ const isRequired = inputSchema.required?.includes(key) || false;
49
+ let promptType = 'input';
50
+ let validate = undefined;
51
+ // Set up validation and input type based on schema
52
+ switch (property.type) {
53
+ case "string":
54
+ if (isRequired) {
55
+ validate = (input) => input.trim() !== "" || `${key} is required`;
56
+ }
57
+ break;
58
+ case "number":
59
+ promptType = 'number';
60
+ if (isRequired) {
61
+ validate = (input) => !isNaN(input) || `${key} must be a valid number`;
62
+ }
63
+ break;
64
+ case "boolean":
65
+ promptType = 'confirm';
66
+ break;
67
+ default:
68
+ if (isRequired) {
69
+ validate = (input) => input.trim() !== "" || `${key} is required`;
70
+ }
71
+ }
72
+ inputPrompts.push({
73
+ type: promptType,
74
+ name: key,
75
+ message: `${key}${isRequired ? " (required)" : " (optional)"}:`,
76
+ validate: validate,
77
+ when: isRequired ? true : (answers) => {
78
+ // For optional fields, ask if user wants to provide a value
79
+ return true;
80
+ }
81
+ });
82
+ }
83
+ const answers = await inquirer.prompt(inputPrompts);
84
+ // Convert answers to proper types
85
+ for (const [key, value] of Object.entries(answers)) {
86
+ const property = inputSchema.properties[key];
87
+ switch (property.type) {
88
+ case "string":
89
+ inputValues[key] = value;
90
+ break;
91
+ case "number":
92
+ inputValues[key] = parseFloat(value);
93
+ break;
94
+ case "boolean":
95
+ inputValues[key] = value;
96
+ break;
97
+ default:
98
+ inputValues[key] = value;
99
+ }
100
+ }
101
+ }
102
+ console.log("\n🚀 Executing tool...");
103
+ console.log(`Input: ${JSON.stringify(inputValues, null, 2)}`);
104
+ // Get the execute function string directly from the selected tool
105
+ const toolCode = selectedTool.execute;
106
+ // Execute the tool
107
+ try {
108
+ // Create a temporary CommonJS file to run the tool
109
+ const tempFile = path.join(luaDir, `temp-${selectedTool.name}.cjs`);
110
+ const commonJsWrapper = `
111
+ const executeFunction = ${toolCode};
112
+
113
+ // Export the function for testing
114
+ module.exports = async (input) => {
115
+ return await executeFunction(input);
116
+ };
117
+ `;
118
+ fs.writeFileSync(tempFile, commonJsWrapper);
119
+ // Import and execute the CommonJS module
120
+ const { createRequire } = await import('module');
121
+ const require = createRequire(import.meta.url);
122
+ const executeFunction = require(tempFile);
123
+ const result = await executeFunction(inputValues);
124
+ // Clean up temp file
125
+ fs.unlinkSync(tempFile);
126
+ console.log("\n✅ Tool execution successful!");
127
+ console.log(`Output: ${JSON.stringify(result, null, 2)}`);
128
+ }
129
+ catch (executionError) {
130
+ console.error("\n❌ Tool execution failed:");
131
+ console.error(executionError.message);
132
+ if (executionError.stack) {
133
+ console.error(executionError.stack);
134
+ }
135
+ }
136
+ }
137
+ catch (error) {
138
+ console.error("❌ Test failed:", error.message);
139
+ process.exit(1);
140
+ }
141
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from "commander";
3
- import { configureCommand, initCommand, destroyCommand, apiKeyCommand, agentsCommand } from "./commands/index.js";
3
+ import { configureCommand, initCommand, destroyCommand, apiKeyCommand, agentsCommand, deployCommand, testCommand } from "./commands/index.js";
4
4
  const program = new Command();
5
5
  program
6
6
  .command("init")
@@ -22,4 +22,12 @@ program
22
22
  .command("agents")
23
23
  .description("Fetch agents from HeyLua API")
24
24
  .action(agentsCommand);
25
+ program
26
+ .command("compile")
27
+ .description("Compile Lua skill to generate deployable format")
28
+ .action(deployCommand);
29
+ program
30
+ .command("test")
31
+ .description("Test Lua skill tools interactively")
32
+ .action(testCommand);
25
33
  program.parse(process.argv);
@@ -0,0 +1,8 @@
1
+ import { UserData } from "../types/index.js";
2
+ export declare function saveApiKey(apiKey: string): Promise<void>;
3
+ export declare function loadApiKey(): Promise<string | null>;
4
+ export declare function deleteApiKey(): Promise<boolean>;
5
+ export declare function checkApiKey(apiKey: string): Promise<UserData>;
6
+ export declare function requestEmailOTP(email: string): Promise<boolean>;
7
+ export declare function verifyOTPAndGetToken(email: string, pin: string): Promise<string | null>;
8
+ export declare function generateApiKey(signInToken: string): Promise<string | null>;
@@ -0,0 +1,10 @@
1
+ import { ZodType } from "zod";
2
+ import { LuaTool } from "./types/index.js";
3
+ export { LuaTool };
4
+ export declare class LuaSkill {
5
+ private readonly apiKey;
6
+ private readonly tools;
7
+ constructor(apiKey: string);
8
+ addTool<TInput extends ZodType, TOutput extends ZodType>(tool: LuaTool<TInput, TOutput>): void;
9
+ run(input: Record<string, any>): Promise<any>;
10
+ }
package/dist/skill.js ADDED
@@ -0,0 +1,18 @@
1
+ export class LuaSkill {
2
+ constructor(apiKey) {
3
+ this.apiKey = apiKey;
4
+ this.tools = [];
5
+ }
6
+ addTool(tool) {
7
+ this.tools.push(tool);
8
+ }
9
+ async run(input) {
10
+ const tool = this.tools.find(tool => tool.name === input.tool);
11
+ if (!tool) {
12
+ throw new Error(`Tool ${input.tool} not found`);
13
+ }
14
+ // Validate input against the tool's schema
15
+ const validatedInput = tool.inputSchema.parse(input);
16
+ return tool.execute(validatedInput);
17
+ }
18
+ }
@@ -0,0 +1,69 @@
1
+ export interface UserData {
2
+ uid: string;
3
+ email: string;
4
+ emailVerified: boolean;
5
+ fullName: string;
6
+ mobileNumbers: any[];
7
+ emailAddresses: EmailAddress[];
8
+ country: Country;
9
+ admin: Admin;
10
+ channels: Record<string, any>;
11
+ rights: Record<string, any>;
12
+ setupPersona: Record<string, any>;
13
+ notifications: Record<string, any>;
14
+ }
15
+ export interface EmailAddress {
16
+ address: string;
17
+ validated: boolean;
18
+ validatedAt: number;
19
+ _id: string;
20
+ }
21
+ export interface Country {
22
+ code: string;
23
+ name: string;
24
+ }
25
+ export interface Admin {
26
+ userId: string;
27
+ orgs: Organization[];
28
+ id: string;
29
+ createdAt: number;
30
+ __v: number;
31
+ }
32
+ export interface Organization {
33
+ id: string;
34
+ rights?: string[];
35
+ agents: Agent[];
36
+ registeredName: string;
37
+ country: string;
38
+ phoneNumber?: string | null;
39
+ type: string;
40
+ }
41
+ export interface Agent {
42
+ agentId: string;
43
+ rights: string[];
44
+ name: string;
45
+ }
46
+ export interface OTPResponse {
47
+ signInToken: string;
48
+ }
49
+ export interface ApiKeyResponse {
50
+ message: string;
51
+ userId: string;
52
+ apiKey: string;
53
+ apiId: string;
54
+ }
55
+ import { ZodType } from "zod";
56
+ export interface LuaTool<TInput extends ZodType = ZodType, TOutput extends ZodType = ZodType> {
57
+ name: string;
58
+ description: string;
59
+ inputSchema: TInput;
60
+ outputSchema: TOutput;
61
+ execute: (input: any) => Promise<any>;
62
+ }
63
+ export declare class LuaSkill {
64
+ private readonly apiKey;
65
+ private readonly tools;
66
+ constructor(apiKey: string);
67
+ addTool<TInput extends ZodType, TOutput extends ZodType>(tool: LuaTool<TInput, TOutput>): void;
68
+ run(input: Record<string, any>): Promise<any>;
69
+ }
@@ -0,0 +1 @@
1
+ export * from "./types/index.js";
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ // Re-export types for external use
2
+ export * from "./types/index.js";
@@ -0,0 +1,2 @@
1
+ export declare function copyTemplateFiles(templateDir: string, targetDir: string): void;
2
+ export declare function createSkillToml(agentId: string, orgId: string, skillName: string, skillDescription: string): void;
@@ -1,19 +1,44 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
+ import { fileURLToPath } from "url";
3
4
  export function copyTemplateFiles(templateDir, targetDir) {
4
5
  const files = fs.readdirSync(templateDir);
5
6
  for (const file of files) {
7
+ // Skip node_modules and package-lock.json to avoid circular dependencies
8
+ if (file === 'node_modules' || file === 'package-lock.json') {
9
+ continue;
10
+ }
6
11
  const srcPath = path.join(templateDir, file);
7
12
  const destPath = path.join(targetDir, file);
8
13
  if (fs.statSync(srcPath).isDirectory()) {
9
14
  fs.mkdirSync(destPath, { recursive: true });
10
15
  copyTemplateFiles(srcPath, destPath);
11
16
  }
17
+ else if (file === 'package.json') {
18
+ // Special handling for package.json to update lua-cli version
19
+ updatePackageJson(srcPath, destPath);
20
+ }
12
21
  else {
13
22
  fs.copyFileSync(srcPath, destPath);
14
23
  }
15
24
  }
16
25
  }
26
+ function updatePackageJson(srcPath, destPath) {
27
+ // Read the template package.json
28
+ const templatePackageJson = JSON.parse(fs.readFileSync(srcPath, 'utf8'));
29
+ // Get the current CLI version from the main package.json
30
+ const __filename = fileURLToPath(import.meta.url);
31
+ const __dirname = path.dirname(__filename);
32
+ const mainPackageJsonPath = path.join(__dirname, '..', '..', 'package.json');
33
+ const mainPackageJson = JSON.parse(fs.readFileSync(mainPackageJsonPath, 'utf8'));
34
+ const currentCliVersion = mainPackageJson.version;
35
+ // Update the lua-cli dependency version
36
+ if (templatePackageJson.dependencies && templatePackageJson.dependencies['lua-cli']) {
37
+ templatePackageJson.dependencies['lua-cli'] = `^${currentCliVersion}`;
38
+ }
39
+ // Write the updated package.json
40
+ fs.writeFileSync(destPath, JSON.stringify(templatePackageJson, null, 2) + '\n');
41
+ }
17
42
  export function createSkillToml(agentId, orgId, skillName, skillDescription) {
18
43
  const tomlContent = `[agent]
19
44
  agentId = "${agentId}"
package/package.json CHANGED
@@ -1,13 +1,21 @@
1
1
  {
2
2
  "name": "lua-cli",
3
- "version": "1.1.2",
3
+ "version": "1.1.4-beta.0",
4
4
  "description": "Command-line interface for Lua AI platform - manage agents, organizations, and skills",
5
5
  "readmeFilename": "README.md",
6
6
  "main": "dist/index.js",
7
+ "exports": {
8
+ ".": "./dist/index.js",
9
+ "./types": "./dist/types.js",
10
+ "./skill": "./dist/skill.js"
11
+ },
7
12
  "scripts": {
8
- "build": "tsc",
13
+ "clean": "rm -rf dist",
14
+ "build": "npm run clean && tsc",
9
15
  "prepublishOnly": "npm run build",
10
- "test": "echo \"Error: no test specified\" && exit 1"
16
+ "test": "jest",
17
+ "test:watch": "jest --watch",
18
+ "test:coverage": "jest --coverage"
11
19
  },
12
20
  "keywords": [
13
21
  "lua",
@@ -36,7 +44,6 @@
36
44
  },
37
45
  "files": [
38
46
  "dist/**/*",
39
- "template/**/*",
40
47
  "README.md",
41
48
  "CHANGELOG.md",
42
49
  "LICENSE"
@@ -45,16 +52,21 @@
45
52
  "commander": "^14.0.1",
46
53
  "inquirer": "^12.9.6",
47
54
  "keytar": "^7.9.0",
48
- "node-fetch": "^3.3.2"
55
+ "node-fetch": "^3.3.2",
56
+ "zod": "^4.1.9"
49
57
  },
50
58
  "devDependencies": {
51
59
  "@types/inquirer": "^9.0.9",
60
+ "@types/jest": "^29.5.8",
52
61
  "@types/node": "^24.5.1",
53
62
  "@types/node-fetch": "^2.6.13",
63
+ "esbuild": "^0.25.10",
64
+ "jest": "^29.7.0",
65
+ "ts-jest": "^29.1.1",
54
66
  "ts-node": "^10.9.2",
55
67
  "typescript": "^5.9.2"
56
68
  },
57
69
  "bin": {
58
70
  "lua": "dist/index.js"
59
71
  }
60
- }
72
+ }
@@ -1,12 +0,0 @@
1
- {
2
- "name": "lua-skill",
3
- "version": "1.0.0",
4
- "description": "",
5
- "license": "ISC",
6
- "author": "",
7
- "type": "commonjs",
8
- "main": "index.ts",
9
- "scripts": {
10
- "test": "echo \"Error: no test specified\" && exit 1"
11
- }
12
- }