lua-cli 1.3.0-alpha.1 → 1.3.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.
Files changed (42) hide show
  1. package/CHANGELOG.md +8 -3
  2. package/README.md +168 -14
  3. package/dist/commands/agents.js +5 -9
  4. package/dist/commands/compile.js +252 -70
  5. package/dist/commands/deploy-new.d.ts +0 -20
  6. package/dist/commands/deploy-new.js +130 -128
  7. package/dist/commands/deploy.js +15 -43
  8. package/dist/commands/dev.d.ts +63 -0
  9. package/dist/commands/dev.js +656 -0
  10. package/dist/commands/index.d.ts +1 -0
  11. package/dist/commands/index.js +1 -0
  12. package/dist/commands/init.js +230 -42
  13. package/dist/commands/push.js +25 -36
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.js +7 -1
  16. package/dist/services/api.d.ts +195 -0
  17. package/dist/services/api.js +209 -0
  18. package/dist/services/auth.d.ts +82 -0
  19. package/dist/services/auth.js +101 -51
  20. package/dist/user-data-api.d.ts +52 -0
  21. package/dist/user-data-api.js +151 -0
  22. package/dist/utils/files.d.ts +4 -1
  23. package/dist/utils/files.js +62 -16
  24. package/dist/web/app.css +1050 -0
  25. package/dist/web/app.js +79 -0
  26. package/dist/web/tools-page.css +377 -0
  27. package/package.json +17 -4
  28. package/template/package-lock.json +32 -3
  29. package/template/package.json +3 -1
  30. package/template/{index.ts → src/index.ts} +9 -3
  31. package/template/src/tools/UserPreferencesTool.ts +73 -0
  32. package/template/tools/UserPreferencesTool.ts +73 -0
  33. package/template/tsconfig.json +1 -1
  34. package/template/.lua/deploy.json +0 -148
  35. /package/template/{services → src/services}/ApiService.ts +0 -0
  36. /package/template/{services → src/services}/GetWeather.ts +0 -0
  37. /package/template/{services → src/services}/MathService.ts +0 -0
  38. /package/template/{tools → src/tools}/AdvancedMathTool.ts +0 -0
  39. /package/template/{tools → src/tools}/CalculatorTool.ts +0 -0
  40. /package/template/{tools → src/tools}/CreatePostTool.ts +0 -0
  41. /package/template/{tools → src/tools}/GetUserDataTool.ts +0 -0
  42. /package/template/{tools → src/tools}/GetWeatherTool.ts +0 -0
@@ -3,6 +3,8 @@ import path from "path";
3
3
  import { gzipSync, gunzipSync } from "zlib";
4
4
  import { Buffer } from "buffer";
5
5
  import { withErrorHandling, writeProgress, writeSuccess } from "../utils/cli.js";
6
+ import { readSkillConfig } from '../utils/files.js';
7
+ import * as ts from "typescript";
6
8
  // Compression utilities
7
9
  function compressCode(code) {
8
10
  const compressed = gzipSync(code);
@@ -22,23 +24,29 @@ export async function compileCommand() {
22
24
  }
23
25
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
24
26
  const packageVersion = packageJson.version || "1.0.0";
25
- const skillsName = packageJson.name || "lua-skill";
26
- // Read index.ts file
27
- const indexPath = path.join(process.cwd(), "index.ts");
27
+ // Get skill name from config file, fallback to package.json name
28
+ const config = readSkillConfig();
29
+ const skillName = config?.skill?.name || packageJson.name || "lua-skill";
30
+ // Read index.ts file (check both root and src directory)
31
+ let indexPath = path.join(process.cwd(), "index.ts");
28
32
  if (!fs.existsSync(indexPath)) {
29
- throw new Error("index.ts not found in current directory");
33
+ indexPath = path.join(process.cwd(), "src", "index.ts");
34
+ if (!fs.existsSync(indexPath)) {
35
+ throw new Error("index.ts not found in current directory or src/ directory");
36
+ }
30
37
  }
31
38
  const indexContent = fs.readFileSync(indexPath, "utf8");
32
39
  // Extract skill information
33
- const skillInfo = await extractSkillInfo(indexContent);
34
- // Extract skill metadata from LuaSkill constructor and TOML file
40
+ const skillInfo = await extractSkillInfo(indexContent, indexPath);
41
+ // Extract skill metadata from LuaSkill constructor and config file
35
42
  const skillMetadata = await extractSkillMetadata(indexContent);
36
- // Use version from TOML file, fallback to package.json version
43
+ // Use version from config file, fallback to package.json version
37
44
  const version = skillMetadata.version || packageVersion || "1.0.0";
38
45
  // Create deployment data with compressed execute code
39
46
  const deployData = {
40
47
  version,
41
- skillsName,
48
+ name: skillName,
49
+ skillsName: skillName,
42
50
  skillId: skillMetadata.skillId,
43
51
  description: skillMetadata.description,
44
52
  context: skillMetadata.context,
@@ -63,50 +71,224 @@ export async function compileCommand() {
63
71
  writeSuccess("✅ Skill compiled successfully");
64
72
  }, "compilation");
65
73
  }
66
- async function extractSkillInfo(indexContent) {
74
+ async function extractSkillInfo(indexContent, indexFilePath) {
67
75
  const tools = [];
68
- // Find inline addTool calls: skill.addTool({...})
69
- const inlineAddToolRegex = /skill\.addTool\(\s*\{([\s\S]*?)\}\s*\)/g;
70
- let match;
71
- while ((match = inlineAddToolRegex.exec(indexContent)) !== null) {
72
- const toolContent = match[1];
73
- // Extract tool properties
74
- const nameMatch = toolContent.match(/name:\s*["']([^"']+)["']/);
75
- const descriptionMatch = toolContent.match(/description:\s*["']([^"']+)["']/);
76
- const inputSchemaMatch = toolContent.match(/inputSchema:\s*(\w+)/);
77
- const outputSchemaMatch = toolContent.match(/outputSchema:\s*(\w+)/);
78
- const executeMatch = toolContent.match(/execute:\s*async\s*\([^)]*\)\s*=>\s*\{([\s\S]*?)\}/);
79
- if (nameMatch && descriptionMatch && inputSchemaMatch && outputSchemaMatch && executeMatch) {
80
- const toolName = nameMatch[1];
81
- const toolDescription = descriptionMatch[1];
82
- const inputSchemaVar = inputSchemaMatch[1];
83
- const outputSchemaVar = outputSchemaMatch[1];
84
- const executeBody = executeMatch[1];
85
- // Convert schemas to JSON Schema format
86
- const inputSchema = convertSchemaToJSON(inputSchemaVar, indexContent);
87
- const outputSchema = convertSchemaToJSON(outputSchemaVar, indexContent);
88
- // Create self-contained execute function
89
- const selfContainedExecute = await createSelfContainedExecute(executeBody, indexContent);
90
- tools.push({
91
- name: toolName,
92
- description: toolDescription,
93
- inputSchema,
94
- outputSchema,
95
- execute: selfContainedExecute
96
- });
97
- }
98
- }
99
- // Find class-based addTool calls: skill.addTool(new SomeTool())
100
- const classAddToolRegex = /skill\.addTool\(\s*new\s+(\w+)\(\)\s*\)/g;
101
- let classMatch;
102
- while ((classMatch = classAddToolRegex.exec(indexContent)) !== null) {
103
- const className = classMatch[1];
104
- // Find the tool class definition
105
- const toolInfo = await extractToolFromClass(className, indexContent);
76
+ const toolArguments = [];
77
+ try {
78
+ // Create a TypeScript source file
79
+ const sourceFile = ts.createSourceFile(indexFilePath, indexContent, ts.ScriptTarget.Latest, true);
80
+ // Traverse the AST to find skill.addTool() calls
81
+ function visit(node) {
82
+ // Check for skill.addTool() calls
83
+ if (ts.isCallExpression(node) &&
84
+ ts.isPropertyAccessExpression(node.expression) &&
85
+ ts.isIdentifier(node.expression.expression) &&
86
+ node.expression.expression.text === 'skill' &&
87
+ ts.isIdentifier(node.expression.name) &&
88
+ node.expression.name.text === 'addTool') {
89
+ // Check if this call is commented out
90
+ if (isNodeCommentedOut(node, sourceFile)) {
91
+ return;
92
+ }
93
+ // Collect the tool argument for later processing
94
+ if (node.arguments.length > 0) {
95
+ const toolArgument = node.arguments[0];
96
+ toolArguments.push(toolArgument);
97
+ }
98
+ }
99
+ // Continue traversing
100
+ ts.forEachChild(node, visit);
101
+ }
102
+ visit(sourceFile);
103
+ // Process all collected tool arguments
104
+ for (const toolArgument of toolArguments) {
105
+ await processToolArgument(toolArgument, sourceFile, indexContent, tools);
106
+ }
107
+ }
108
+ catch (error) {
109
+ console.warn('Warning: Could not parse TypeScript AST, falling back to regex approach:', error);
110
+ // Fallback to the old regex approach if AST parsing fails
111
+ return await extractSkillInfoRegex(indexContent, indexFilePath);
112
+ }
113
+ return tools;
114
+ }
115
+ function isNodeCommentedOut(node, sourceFile) {
116
+ const nodeStart = node.getStart(sourceFile);
117
+ const nodeLine = sourceFile.getLineAndCharacterOfPosition(nodeStart).line;
118
+ // Get the text of the line up to the node
119
+ const lineStart = sourceFile.getPositionOfLineAndCharacter(nodeLine, 0);
120
+ const lineText = sourceFile.text.substring(lineStart, nodeStart);
121
+ // Check if the line starts with a comment
122
+ const trimmedLine = lineText.trim();
123
+ return trimmedLine.startsWith('//') ||
124
+ trimmedLine.startsWith('*') ||
125
+ trimmedLine.startsWith('#');
126
+ }
127
+ async function processToolArgument(toolArgument, sourceFile, indexContent, tools) {
128
+ if (ts.isObjectLiteralExpression(toolArgument)) {
129
+ // Inline tool definition: skill.addTool({...})
130
+ await processInlineTool(toolArgument, sourceFile, indexContent, tools);
131
+ }
132
+ else if (ts.isNewExpression(toolArgument)) {
133
+ // Class-based tool: skill.addTool(new SomeTool())
134
+ await processClassBasedTool(toolArgument, sourceFile, indexContent, tools);
135
+ }
136
+ }
137
+ async function processInlineTool(objectNode, sourceFile, indexContent, tools) {
138
+ const toolProperties = {};
139
+ // Extract properties from the object
140
+ for (const property of objectNode.properties) {
141
+ if (ts.isPropertyAssignment(property) &&
142
+ ts.isIdentifier(property.name) &&
143
+ property.initializer) {
144
+ const key = property.name.text;
145
+ const value = property.initializer;
146
+ if (key === 'name' && ts.isStringLiteral(value)) {
147
+ toolProperties.name = value.text;
148
+ }
149
+ else if (key === 'description' && ts.isStringLiteral(value)) {
150
+ toolProperties.description = value.text;
151
+ }
152
+ else if (key === 'inputSchema' && ts.isIdentifier(value)) {
153
+ toolProperties.inputSchemaVar = value.text;
154
+ }
155
+ else if (key === 'outputSchema' && ts.isIdentifier(value)) {
156
+ toolProperties.outputSchemaVar = value.text;
157
+ }
158
+ else if (key === 'execute' && ts.isArrowFunction(value)) {
159
+ // Extract the function body
160
+ if (ts.isBlock(value.body)) {
161
+ const bodyText = extractFunctionBody(value.body, sourceFile);
162
+ toolProperties.executeBody = bodyText;
163
+ }
164
+ }
165
+ }
166
+ }
167
+ // Create the tool if all required properties are present
168
+ if (toolProperties.name &&
169
+ toolProperties.description &&
170
+ toolProperties.inputSchemaVar &&
171
+ toolProperties.outputSchemaVar &&
172
+ toolProperties.executeBody) {
173
+ const inputSchema = convertSchemaToJSON(toolProperties.inputSchemaVar, indexContent);
174
+ const outputSchema = convertSchemaToJSON(toolProperties.outputSchemaVar, indexContent);
175
+ const selfContainedExecute = await createSelfContainedExecute(toolProperties.executeBody, indexContent);
176
+ tools.push({
177
+ name: toolProperties.name,
178
+ description: toolProperties.description,
179
+ inputSchema,
180
+ outputSchema,
181
+ execute: selfContainedExecute
182
+ });
183
+ }
184
+ }
185
+ async function processClassBasedTool(newNode, sourceFile, indexContent, tools) {
186
+ if (ts.isIdentifier(newNode.expression)) {
187
+ const className = newNode.expression.text;
188
+ const toolInfo = await extractToolFromClass(className, indexContent, sourceFile.fileName);
106
189
  if (toolInfo) {
107
190
  tools.push(toolInfo);
108
191
  }
109
192
  }
193
+ }
194
+ function extractFunctionBody(blockStatement, sourceFile) {
195
+ // Extract the function body text from the AST
196
+ const start = blockStatement.getStart(sourceFile);
197
+ const end = blockStatement.getEnd();
198
+ // Get the text between the braces
199
+ const fullText = sourceFile.text.substring(start, end);
200
+ // Remove the outer braces and return the inner content
201
+ if (fullText.startsWith('{') && fullText.endsWith('}')) {
202
+ return fullText.slice(1, -1).trim();
203
+ }
204
+ return fullText;
205
+ }
206
+ // Fallback regex-based extraction (renamed from the original function)
207
+ async function extractSkillInfoRegex(indexContent, indexFilePath) {
208
+ const tools = [];
209
+ // Find all addTool calls by searching for the pattern and manually parsing
210
+ let searchIndex = 0;
211
+ while (true) {
212
+ const addToolIndex = indexContent.indexOf('skill.addTool(', searchIndex);
213
+ if (addToolIndex === -1)
214
+ break;
215
+ // Check if this line is commented out
216
+ const lineStart = indexContent.lastIndexOf('\n', addToolIndex) + 1;
217
+ const lineContent = indexContent.substring(lineStart, addToolIndex).trim();
218
+ if (lineContent.startsWith('//') || lineContent.startsWith('*') || lineContent.startsWith('#')) {
219
+ searchIndex = addToolIndex + 1;
220
+ continue;
221
+ }
222
+ // Find the matching closing parenthesis
223
+ let parenCount = 0;
224
+ let braceCount = 0;
225
+ let callStart = addToolIndex + 'skill.addTool('.length;
226
+ let callEnd = callStart;
227
+ let found = false;
228
+ for (let i = callStart; i < indexContent.length; i++) {
229
+ const char = indexContent[i];
230
+ if (char === '(')
231
+ parenCount++;
232
+ else if (char === ')') {
233
+ if (parenCount === 0) {
234
+ callEnd = i;
235
+ found = true;
236
+ break;
237
+ }
238
+ parenCount--;
239
+ }
240
+ else if (char === '{')
241
+ braceCount++;
242
+ else if (char === '}')
243
+ braceCount--;
244
+ }
245
+ if (!found) {
246
+ searchIndex = addToolIndex + 1;
247
+ continue;
248
+ }
249
+ const toolArgument = indexContent.substring(callStart, callEnd).trim();
250
+ // Check if it's an inline tool definition: skill.addTool({...})
251
+ if (toolArgument.startsWith('{')) {
252
+ // Extract tool properties
253
+ const nameMatch = toolArgument.match(/name:\s*["']([^"']+)["']/);
254
+ const descriptionMatch = toolArgument.match(/description:\s*["']([^"']+)["']/);
255
+ const inputSchemaMatch = toolArgument.match(/inputSchema:\s*(\w+)/);
256
+ const outputSchemaMatch = toolArgument.match(/outputSchema:\s*(\w+)/);
257
+ const executeMatch = toolArgument.match(/execute:\s*async\s*\([^)]*\)\s*=>\s*\{([\s\S]*?)\}/);
258
+ if (nameMatch && descriptionMatch && inputSchemaMatch && outputSchemaMatch && executeMatch) {
259
+ const toolName = nameMatch[1];
260
+ const toolDescription = descriptionMatch[1];
261
+ const inputSchemaVar = inputSchemaMatch[1];
262
+ const outputSchemaVar = outputSchemaMatch[1];
263
+ const executeBody = executeMatch[1];
264
+ // Convert schemas to JSON Schema format
265
+ const inputSchema = convertSchemaToJSON(inputSchemaVar, indexContent);
266
+ const outputSchema = convertSchemaToJSON(outputSchemaVar, indexContent);
267
+ // Create self-contained execute function
268
+ const selfContainedExecute = await createSelfContainedExecute(executeBody, indexContent);
269
+ tools.push({
270
+ name: toolName,
271
+ description: toolDescription,
272
+ inputSchema,
273
+ outputSchema,
274
+ execute: selfContainedExecute
275
+ });
276
+ }
277
+ }
278
+ // Check if it's a class-based tool: skill.addTool(new SomeTool())
279
+ else if (toolArgument.startsWith('new ')) {
280
+ const classMatch = toolArgument.match(/new\s+(\w+)\(\)/);
281
+ if (classMatch) {
282
+ const className = classMatch[1];
283
+ // Find the tool class definition
284
+ const toolInfo = await extractToolFromClass(className, indexContent, indexFilePath);
285
+ if (toolInfo) {
286
+ tools.push(toolInfo);
287
+ }
288
+ }
289
+ }
290
+ searchIndex = callEnd + 1;
291
+ }
110
292
  return tools;
111
293
  }
112
294
  function convertSchemaToJSON(schemaVar, indexContent) {
@@ -558,7 +740,7 @@ const axios = {
558
740
  };
559
741
  `;
560
742
  }
561
- async function extractToolFromClass(className, indexContent) {
743
+ async function extractToolFromClass(className, indexContent, indexFilePath) {
562
744
  // Find the import statement for this class
563
745
  const importRegex = new RegExp(`import\\s+${className}\\s+from\\s+["']([^"']+)["']`, 'g');
564
746
  const importMatch = importRegex.exec(indexContent);
@@ -567,8 +749,17 @@ async function extractToolFromClass(className, indexContent) {
567
749
  return null;
568
750
  }
569
751
  const importPath = importMatch[1];
570
- // Read the tool file
571
- const toolFilePath = path.join(process.cwd(), importPath.replace('./', '') + '.ts');
752
+ // Read the tool file - handle both relative and absolute paths
753
+ let toolFilePath;
754
+ if (importPath.startsWith('./') || importPath.startsWith('../')) {
755
+ // Relative path - resolve from the index file directory
756
+ const indexDir = path.dirname(indexFilePath);
757
+ toolFilePath = path.join(indexDir, importPath + '.ts');
758
+ }
759
+ else {
760
+ // Absolute path or just filename
761
+ toolFilePath = path.join(process.cwd(), importPath + '.ts');
762
+ }
572
763
  if (!fs.existsSync(toolFilePath)) {
573
764
  console.warn(`Warning: Tool file not found: ${toolFilePath}`);
574
765
  return null;
@@ -583,7 +774,7 @@ async function extractToolFromClass(className, indexContent) {
583
774
  }
584
775
  const toolName = nameMatch[1];
585
776
  const toolDescription = descriptionMatch[1];
586
- // Extract schemas
777
+ // Extract schemas - look for multiple schema definitions
587
778
  const inputSchemaMatch = toolContent.match(/const\s+(\w+)\s*=\s*z\.object\(/);
588
779
  const outputSchemaMatch = toolContent.match(/const\s+(\w+)\s*=\s*z\.object\(/);
589
780
  if (!inputSchemaMatch) {
@@ -604,7 +795,7 @@ async function extractToolFromClass(className, indexContent) {
604
795
  // 1. The service classes
605
796
  // 2. Class instantiation
606
797
  // 3. The execute logic
607
- const selfContainedExecute = await createClassBasedExecute(executeBody, toolContent, className);
798
+ const selfContainedExecute = await createClassBasedExecute(executeBody, toolContent, className, toolFilePath);
608
799
  return {
609
800
  name: toolName,
610
801
  description: toolDescription,
@@ -613,7 +804,7 @@ async function extractToolFromClass(className, indexContent) {
613
804
  execute: selfContainedExecute
614
805
  };
615
806
  }
616
- async function createClassBasedExecute(executeBody, toolContent, className) {
807
+ async function createClassBasedExecute(executeBody, toolContent, className, toolFilePath) {
617
808
  const dependencies = [];
618
809
  const bundledPackages = new Set();
619
810
  // 1. Parse imports from the tool file
@@ -654,11 +845,9 @@ async function createClassBasedExecute(executeBody, toolContent, className) {
654
845
  }
655
846
  // Handle local service imports
656
847
  if (packagePath.startsWith('./') || packagePath.startsWith('../')) {
657
- // The tool files are in tools/ subdirectory, so we need to resolve from there
658
- const toolDir = path.join(process.cwd(), 'tools');
659
- // Resolve the service file path correctly
660
- // If the import is ../services/ApiService, resolve it relative to the tools directory
661
- const serviceFilePath = path.resolve(process.cwd(), 'tools', packagePath + '.ts');
848
+ // Resolve the service file path relative to the tool file location
849
+ const toolDir = path.dirname(toolFilePath);
850
+ const serviceFilePath = path.resolve(toolDir, packagePath + '.ts');
662
851
  if (fs.existsSync(serviceFilePath)) {
663
852
  const serviceContent = fs.readFileSync(serviceFilePath, 'utf8');
664
853
  // Process all imports in the service file
@@ -779,20 +968,13 @@ ${dependencyCode ? dependencyCode + '\n' : ''}${finalExecuteBody}
779
968
  }`;
780
969
  }
781
970
  async function extractSkillMetadata(indexContent) {
782
- // Extract skillId and version from TOML file
971
+ // Extract skillId and version from config file
783
972
  let skillId = '';
784
973
  let version = '';
785
- const tomlPath = path.join(process.cwd(), 'lua.skill.toml');
786
- if (fs.existsSync(tomlPath)) {
787
- const tomlContent = fs.readFileSync(tomlPath, 'utf8');
788
- const skillIdMatch = tomlContent.match(/skillId\s*=\s*["']([^"']+)["']/);
789
- if (skillIdMatch) {
790
- skillId = skillIdMatch[1];
791
- }
792
- const versionMatch = tomlContent.match(/version\s*=\s*["']([^"']+)["']/);
793
- if (versionMatch) {
794
- version = versionMatch[1];
795
- }
974
+ const config = readSkillConfig();
975
+ if (config) {
976
+ skillId = config.skill?.skillId || '';
977
+ version = config.skill?.version || '';
796
978
  }
797
979
  // Extract description and context from LuaSkill constructor
798
980
  let description = '';
@@ -1,20 +0,0 @@
1
- export interface VersionInfo {
2
- version: string;
3
- createdDate: string;
4
- createdBy: string;
5
- isCurrent: boolean;
6
- createdByEmail: string;
7
- createdByFullName: string;
8
- }
9
- export interface VersionsResponse {
10
- versions: VersionInfo[];
11
- }
12
- export interface PublishResponse {
13
- message: string;
14
- skillId: string;
15
- activeVersionId: string;
16
- publishedAt: string;
17
- }
18
- export declare function fetchVersions(apiKey: string, agentId: string, skillId: string): Promise<VersionsResponse>;
19
- export declare function publishVersion(apiKey: string, agentId: string, skillId: string, version: string): Promise<PublishResponse>;
20
- export declare function deployCommand(): Promise<void>;