lua-cli 2.2.8-alpha.2 → 2.3.0-alpha.2

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 (195) hide show
  1. package/API_REFERENCE.md +1408 -0
  2. package/CLI_REFERENCE.md +818 -0
  3. package/GETTING_STARTED.md +1040 -0
  4. package/README.md +738 -424
  5. package/TEMPLATE_GUIDE.md +1398 -0
  6. package/dist/api/agent.api.service.d.ts +33 -6
  7. package/dist/api/agent.api.service.js +27 -0
  8. package/dist/api/auth.api.service.d.ts +31 -2
  9. package/dist/api/auth.api.service.js +29 -0
  10. package/dist/api/basket.api.service.d.ts +53 -11
  11. package/dist/api/basket.api.service.js +63 -14
  12. package/dist/api/chat.api.service.d.ts +15 -3
  13. package/dist/api/chat.api.service.js +12 -0
  14. package/dist/api/credentials.d.ts +24 -0
  15. package/dist/api/credentials.js +46 -0
  16. package/dist/api/custom.data.api.service.d.ts +45 -9
  17. package/dist/api/custom.data.api.service.js +43 -9
  18. package/dist/api/lazy-instances.d.ts +49 -0
  19. package/dist/api/lazy-instances.js +95 -0
  20. package/dist/api/order.api.service.d.ts +34 -4
  21. package/dist/api/order.api.service.js +41 -3
  22. package/dist/api/products.api.service.d.ts +39 -9
  23. package/dist/api/products.api.service.js +43 -5
  24. package/dist/api/skills.api.service.d.ts +49 -2
  25. package/dist/api/skills.api.service.js +47 -1
  26. package/dist/api/tool.api.service.d.ts +39 -1
  27. package/dist/api/tool.api.service.js +38 -0
  28. package/dist/api/user.data.api.service.d.ts +23 -1
  29. package/dist/api/user.data.api.service.js +22 -0
  30. package/dist/api-exports.d.ts +236 -5
  31. package/dist/api-exports.js +264 -81
  32. package/dist/cli/command-definitions.d.ts +30 -0
  33. package/dist/cli/command-definitions.js +71 -0
  34. package/dist/commands/agents.d.ts +20 -0
  35. package/dist/commands/agents.js +24 -2
  36. package/dist/commands/apiKey.d.ts +23 -0
  37. package/dist/commands/apiKey.js +23 -0
  38. package/dist/commands/compile.d.ts +24 -0
  39. package/dist/commands/compile.js +67 -759
  40. package/dist/commands/configure.d.ts +24 -0
  41. package/dist/commands/configure.js +31 -96
  42. package/dist/commands/deploy.d.ts +31 -19
  43. package/dist/commands/deploy.js +45 -74
  44. package/dist/commands/destroy.d.ts +27 -0
  45. package/dist/commands/destroy.js +27 -1
  46. package/dist/commands/dev.d.ts +25 -62
  47. package/dist/commands/dev.js +58 -878
  48. package/dist/commands/init.d.ts +27 -0
  49. package/dist/commands/init.js +98 -260
  50. package/dist/commands/push.d.ts +24 -21
  51. package/dist/commands/push.js +39 -92
  52. package/dist/commands/test.d.ts +26 -0
  53. package/dist/commands/test.js +41 -188
  54. package/dist/common/basket.instance.d.ts +54 -3
  55. package/dist/common/basket.instance.js +56 -3
  56. package/dist/common/data.entry.instance.d.ts +25 -2
  57. package/dist/common/data.entry.instance.js +24 -0
  58. package/dist/common/http.client.d.ts +51 -1
  59. package/dist/common/http.client.js +50 -0
  60. package/dist/common/order.instance.d.ts +22 -0
  61. package/dist/common/order.instance.js +31 -4
  62. package/dist/common/product.instance.d.ts +22 -1
  63. package/dist/common/product.instance.js +24 -6
  64. package/dist/common/product.pagination.instance.d.ts +22 -2
  65. package/dist/common/product.pagination.instance.js +22 -1
  66. package/dist/common/product.search.instance.d.ts +13 -3
  67. package/dist/common/product.search.instance.js +12 -1
  68. package/dist/common/user.instance.d.ts +27 -3
  69. package/dist/common/user.instance.js +28 -7
  70. package/dist/config/auth.constants.d.ts +11 -0
  71. package/dist/config/auth.constants.js +11 -0
  72. package/dist/config/compile.constants.d.ts +67 -0
  73. package/dist/config/compile.constants.js +99 -0
  74. package/dist/config/constants.d.ts +5 -0
  75. package/dist/config/constants.js +5 -0
  76. package/dist/config/dev.constants.d.ts +65 -0
  77. package/dist/config/dev.constants.js +79 -0
  78. package/dist/config/init.constants.d.ts +23 -0
  79. package/dist/config/init.constants.js +41 -0
  80. package/dist/index.d.ts +19 -3
  81. package/dist/index.js +28 -44
  82. package/dist/interfaces/admin.d.ts +56 -50
  83. package/dist/interfaces/admin.js +4 -0
  84. package/dist/interfaces/agent.d.ts +21 -0
  85. package/dist/interfaces/agent.js +4 -0
  86. package/dist/interfaces/baskets.d.ts +60 -0
  87. package/dist/interfaces/baskets.js +12 -0
  88. package/dist/interfaces/chat.d.ts +48 -4
  89. package/dist/interfaces/chat.js +4 -0
  90. package/dist/interfaces/common.d.ts +62 -0
  91. package/dist/interfaces/common.js +8 -0
  92. package/dist/interfaces/compile.d.ts +11 -0
  93. package/dist/interfaces/compile.js +4 -0
  94. package/dist/interfaces/custom.data.d.ts +49 -19
  95. package/dist/interfaces/custom.data.js +4 -0
  96. package/dist/interfaces/deploy.d.ts +29 -0
  97. package/dist/interfaces/deploy.js +4 -0
  98. package/dist/interfaces/dev.d.ts +53 -0
  99. package/dist/interfaces/dev.js +5 -0
  100. package/dist/interfaces/init.d.ts +60 -0
  101. package/dist/interfaces/init.js +4 -0
  102. package/dist/interfaces/orders.d.ts +37 -0
  103. package/dist/interfaces/orders.js +12 -0
  104. package/dist/interfaces/product.d.ts +38 -10
  105. package/dist/interfaces/product.js +4 -0
  106. package/dist/interfaces/push.d.ts +26 -0
  107. package/dist/interfaces/push.js +4 -0
  108. package/dist/interfaces/test.d.ts +36 -0
  109. package/dist/interfaces/test.js +4 -0
  110. package/dist/services/auth.d.ts +54 -99
  111. package/dist/services/auth.js +76 -12
  112. package/dist/types/api-contracts.d.ts +211 -0
  113. package/dist/types/api-contracts.js +8 -0
  114. package/dist/types/compile.types.d.ts +76 -0
  115. package/dist/types/compile.types.js +4 -0
  116. package/dist/types/index.d.ts +23 -121
  117. package/dist/types/index.js +25 -14
  118. package/dist/types/skill.d.ts +142 -0
  119. package/dist/{skill.js → types/skill.js} +66 -17
  120. package/dist/types/tool-validation.d.ts +34 -0
  121. package/dist/types/tool-validation.js +42 -0
  122. package/dist/utils/auth-flows.d.ts +26 -0
  123. package/dist/utils/auth-flows.js +141 -0
  124. package/dist/utils/bundling.d.ts +36 -0
  125. package/dist/utils/bundling.js +137 -0
  126. package/dist/utils/compile.d.ts +37 -0
  127. package/dist/utils/compile.js +242 -0
  128. package/dist/utils/deploy-api.d.ts +26 -0
  129. package/dist/utils/deploy-api.js +53 -0
  130. package/dist/utils/deploy-helpers.d.ts +46 -0
  131. package/dist/utils/deploy-helpers.js +86 -0
  132. package/dist/utils/deployment.d.ts +25 -0
  133. package/dist/utils/deployment.js +161 -0
  134. package/dist/utils/dev-api.d.ts +61 -0
  135. package/dist/utils/dev-api.js +262 -0
  136. package/dist/utils/dev-helpers.d.ts +46 -0
  137. package/dist/utils/dev-helpers.js +83 -0
  138. package/dist/utils/dev-server.d.ts +24 -0
  139. package/dist/utils/dev-server.js +555 -0
  140. package/dist/utils/dev-watcher.d.ts +31 -0
  141. package/dist/utils/dev-watcher.js +110 -0
  142. package/dist/utils/files.js +0 -5
  143. package/dist/utils/init-agent.d.ts +34 -0
  144. package/dist/utils/init-agent.js +129 -0
  145. package/dist/utils/init-helpers.d.ts +41 -0
  146. package/dist/utils/init-helpers.js +73 -0
  147. package/dist/utils/init-prompts.d.ts +47 -0
  148. package/dist/utils/init-prompts.js +168 -0
  149. package/dist/utils/push-api.d.ts +15 -0
  150. package/dist/utils/push-api.js +48 -0
  151. package/dist/utils/push-helpers.d.ts +38 -0
  152. package/dist/utils/push-helpers.js +84 -0
  153. package/dist/utils/sandbox-storage.d.ts +27 -0
  154. package/dist/utils/sandbox-storage.js +71 -0
  155. package/dist/utils/sandbox.js +78 -114
  156. package/dist/utils/skill-management.d.ts +14 -0
  157. package/dist/utils/skill-management.js +148 -0
  158. package/dist/utils/test-helpers.d.ts +40 -0
  159. package/dist/utils/test-helpers.js +92 -0
  160. package/dist/utils/test-prompts.d.ts +23 -0
  161. package/dist/utils/test-prompts.js +186 -0
  162. package/dist/utils/tool-detection.d.ts +18 -0
  163. package/dist/utils/tool-detection.js +110 -0
  164. package/dist/web/app.css +941 -17
  165. package/dist/web/app.js +174 -22
  166. package/dist/web/index.html +7 -1
  167. package/package.json +13 -4
  168. package/template/QUICKSTART.md +299 -144
  169. package/template/README.md +928 -349
  170. package/template/TOOL_EXAMPLES.md +655 -0
  171. package/template/package-lock.json +5 -5
  172. package/template/package.json +1 -1
  173. package/template/src/index.ts +147 -207
  174. package/template/src/tools/BasketTool.ts +128 -0
  175. package/template/src/tools/CustomDataTool.ts +7 -13
  176. package/template/src/tools/OrderTool.ts +54 -0
  177. package/template/src/tools/PaymentTool.ts +1 -1
  178. package/template/src/tools/ProductsTool.ts +56 -118
  179. package/template/src/tools/UserDataTool.ts +4 -26
  180. package/dist/common/config.d.ts +0 -5
  181. package/dist/common/config.js +0 -5
  182. package/dist/custom-data-api.d.ts +0 -72
  183. package/dist/custom-data-api.js +0 -174
  184. package/dist/product-api.d.ts +0 -189
  185. package/dist/product-api.js +0 -141
  186. package/dist/services/api.d.ts +0 -549
  187. package/dist/services/api.js +0 -596
  188. package/dist/skill.d.ts +0 -50
  189. package/dist/types.d.ts +0 -1
  190. package/dist/types.js +0 -2
  191. package/dist/user-data-api.d.ts +0 -39
  192. package/dist/user-data-api.js +0 -50
  193. package/template/API.md +0 -604
  194. package/template/DEVELOPER.md +0 -771
  195. package/template/lua.skill.yaml +0 -7
@@ -1,781 +1,89 @@
1
+ /**
2
+ * Compile Command
3
+ * Orchestrates the skill compilation process by coordinating various compilation steps
4
+ */
1
5
  import fs from "fs";
2
6
  import path from "path";
3
- import { gzipSync } from "zlib";
7
+ import { Project } from "ts-morph";
4
8
  import { withErrorHandling, writeProgress, writeSuccess } from "../utils/cli.js";
5
- import { readSkillConfig } from '../utils/files.js';
6
- import { loadApiKey } from '../services/auth.js';
7
- import { ApiService } from '../services/api.js';
8
- import { Project, Node } from "ts-morph";
9
- import { build } from "esbuild";
10
- import yaml from "js-yaml";
11
- // Compression utilities
12
- function compressCode(code) {
13
- const compressed = gzipSync(code);
14
- return compressed.toString('base64');
15
- }
9
+ import { findIndexFile } from '../utils/compile.js';
10
+ import { detectTools } from '../utils/tool-detection.js';
11
+ import { bundleTool, bundleMainIndex, extractExecuteCode } from '../utils/bundling.js';
12
+ import { createDeploymentData, createLegacyDeploymentData } from '../utils/deployment.js';
13
+ import { COMPILE_DIRS, COMPILE_FILES } from '../config/compile.constants.js';
14
+ /**
15
+ * Main compile command - orchestrates the entire skill compilation process.
16
+ *
17
+ * This command performs the following steps:
18
+ * 1. Cleans and prepares output directories (dist/ and .lua/)
19
+ * 2. Analyzes the TypeScript project to detect all tools
20
+ * 3. Bundles each tool individually using esbuild
21
+ * 4. Extracts execute code and schemas from bundled tools
22
+ * 5. Bundles the main index file
23
+ * 6. Creates deployment data in both new and legacy formats
24
+ *
25
+ * Output:
26
+ * - dist/deployment.json - New deployment format with tool references
27
+ * - dist/tools/*.js - Individual bundled tool files
28
+ * - dist/index.js - Main skill entry point
29
+ * - .lua/deploy.json - Legacy deployment format with compressed tools
30
+ * - .lua/*.js - Individual uncompressed tool files for debugging
31
+ *
32
+ * @returns Promise that resolves when compilation is complete
33
+ */
16
34
  export async function compileCommand() {
17
35
  return withErrorHandling(async () => {
18
36
  writeProgress("🔨 Compiling Lua skill...");
19
- // Clean up old directories
20
- const distDir = path.join(process.cwd(), "dist");
21
- const luaDir = path.join(process.cwd(), ".lua");
22
- if (fs.existsSync(distDir)) {
23
- fs.rmSync(distDir, { recursive: true, force: true });
24
- }
25
- if (fs.existsSync(luaDir)) {
26
- fs.rmSync(luaDir, { recursive: true, force: true });
27
- }
28
- // Create directory structures
29
- fs.mkdirSync(distDir, { recursive: true });
30
- fs.mkdirSync(path.join(distDir, "tools"), { recursive: true });
31
- fs.mkdirSync(luaDir, { recursive: true });
32
- // Find index.ts file
37
+ // Step 1: Prepare output directories
38
+ const { distDir, luaDir } = prepareOutputDirectories();
39
+ // Step 2: Analyze TypeScript project and detect tools
33
40
  const indexPath = findIndexFile();
34
- // Use ts-morph to analyze the TypeScript project
35
- const project = new Project({
36
- tsConfigFilePath: path.join(process.cwd(), "tsconfig.json"),
37
- });
38
- // Add the index file to the project
41
+ const project = createTypeScriptProject();
39
42
  const indexFile = project.addSourceFileAtPath(indexPath);
40
- // Detect tools from skill.addTools calls
41
43
  const tools = await detectTools(indexFile, project);
42
44
  writeProgress(`📦 Found ${tools.length} tools to bundle...`);
43
- // Bundle each tool individually and extract execute code
45
+ // Step 3: Bundle each tool and extract metadata
44
46
  for (const tool of tools) {
45
47
  await bundleTool(tool, distDir);
46
48
  await extractExecuteCode(tool, project);
47
49
  }
48
- // Bundle the main index file
50
+ // Step 4: Bundle the main index file
49
51
  await bundleMainIndex(indexPath, distDir);
50
- // Create both deployment formats
52
+ // Step 5: Create deployment data in both formats
51
53
  await createDeploymentData(tools, distDir);
52
54
  await createLegacyDeploymentData(tools, luaDir, indexFile);
53
55
  writeSuccess(`✅ Skill compiled successfully - ${tools.length} tools bundled`);
54
56
  }, "compilation");
55
57
  }
56
- function findIndexFile() {
57
- // Check for index.ts in current directory
58
- let indexPath = path.join(process.cwd(), "index.ts");
59
- if (fs.existsSync(indexPath)) {
60
- return indexPath;
61
- }
62
- // Check for index.ts in src directory
63
- indexPath = path.join(process.cwd(), "src", "index.ts");
64
- if (fs.existsSync(indexPath)) {
65
- return indexPath;
66
- }
67
- throw new Error("index.ts not found in current directory or src/ directory");
68
- }
69
- async function detectTools(indexFile, project) {
70
- const tools = [];
71
- // Find tools in LuaSkill constructors
72
- indexFile.forEachDescendant((node) => {
73
- if (Node.isNewExpression(node)) {
74
- const expression = node.getExpression();
75
- if (expression.getText() === 'LuaSkill') {
76
- const args = node.getArguments();
77
- if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
78
- const configObj = args[0];
79
- // Look for tools property in constructor
80
- configObj.getProperties().forEach((prop) => {
81
- if (Node.isPropertyAssignment(prop) && prop.getName() === 'tools') {
82
- const value = prop.getInitializer();
83
- if (value && Node.isArrayLiteralExpression(value)) {
84
- const toolsArray = value;
85
- toolsArray.getElements().forEach((element) => {
86
- if (Node.isNewExpression(element)) {
87
- const toolInfo = extractToolFromNewExpressionSync(element, project);
88
- if (toolInfo) {
89
- tools.push(toolInfo);
90
- }
91
- }
92
- });
93
- }
94
- }
95
- });
96
- }
97
- }
98
- }
99
- });
100
- // Find all call expressions in the file (addTools method calls)
101
- indexFile.forEachDescendant((node) => {
102
- if (Node.isCallExpression(node)) {
103
- const expression = node.getExpression();
104
- // Check if this is skill.addTools or skill.addTool
105
- if (Node.isPropertyAccessExpression(expression)) {
106
- const object = expression.getExpression();
107
- const property = expression.getName();
108
- if (property === 'addTools' || property === 'addTool') {
109
- const args = node.getArguments();
110
- if (property === 'addTools' && args.length > 0) {
111
- // Handle skill.addTools([...]) - array of tools
112
- const arrayArg = args[0];
113
- if (Node.isArrayLiteralExpression(arrayArg)) {
114
- const elements = arrayArg.getElements();
115
- for (const element of elements) {
116
- if (Node.isNewExpression(element)) {
117
- const toolInfo = extractToolFromNewExpressionSync(element, project);
118
- if (toolInfo) {
119
- tools.push(toolInfo);
120
- }
121
- }
122
- }
123
- }
124
- }
125
- else if (property === 'addTool' && args.length > 0) {
126
- // Handle skill.addTool(new ToolClass()) - single tool
127
- const arg = args[0];
128
- if (Node.isNewExpression(arg)) {
129
- const toolInfo = extractToolFromNewExpressionSync(arg, project);
130
- if (toolInfo) {
131
- tools.push(toolInfo);
132
- }
133
- }
134
- }
135
- }
136
- }
137
- }
138
- });
139
- return tools;
140
- }
141
- function extractToolFromNewExpressionSync(newExpr, project) {
142
- try {
143
- const expression = newExpr.getExpression();
144
- const className = expression.getText();
145
- // Find the import declaration for this class
146
- const sourceFile = newExpr.getSourceFile();
147
- const imports = sourceFile.getImportDeclarations();
148
- for (const importDecl of imports) {
149
- const namedImports = importDecl.getNamedImports();
150
- const defaultImport = importDecl.getDefaultImport();
151
- let toolFilePath = null;
152
- // Check named imports
153
- for (const namedImport of namedImports) {
154
- if (namedImport.getName() === className) {
155
- toolFilePath = resolveImportPath(importDecl.getModuleSpecifierValue(), sourceFile.getFilePath());
156
- break;
157
- }
158
- }
159
- // Check default import
160
- if (!toolFilePath && defaultImport && defaultImport.getText() === className) {
161
- toolFilePath = resolveImportPath(importDecl.getModuleSpecifierValue(), sourceFile.getFilePath());
162
- }
163
- if (toolFilePath && fs.existsSync(toolFilePath)) {
164
- // Extract tool metadata from the class file
165
- try {
166
- const toolSourceFile = project.addSourceFileAtPath(toolFilePath);
167
- const classDecl = toolSourceFile.getClass(className);
168
- if (classDecl) {
169
- const nameProperty = classDecl.getProperty('name');
170
- const descProperty = classDecl.getProperty('description');
171
- let toolName = className.replace(/Tool$/, '').toLowerCase();
172
- let description = '';
173
- // Extract name from property if available
174
- if (nameProperty && nameProperty.getInitializer()) {
175
- const nameValue = nameProperty.getInitializer()?.getText();
176
- if (nameValue) {
177
- toolName = nameValue.replace(/['"]/g, '');
178
- }
179
- }
180
- // Extract description from property if available
181
- if (descProperty && descProperty.getInitializer()) {
182
- const descValue = descProperty.getInitializer()?.getText();
183
- if (descValue) {
184
- description = descValue.replace(/['"]/g, '');
185
- }
186
- }
187
- const toolInfo = {
188
- name: toolName,
189
- className,
190
- filePath: toolFilePath,
191
- description
192
- };
193
- return toolInfo;
194
- }
195
- }
196
- catch (fileError) {
197
- console.warn(`Warning: Could not load tool file ${toolFilePath}:`, fileError);
198
- }
199
- }
200
- }
201
- return null;
202
- }
203
- catch (error) {
204
- console.warn(`Warning: Could not extract tool info for ${newExpr.getText()}:`, error);
205
- return null;
206
- }
207
- }
208
- function resolveImportPath(moduleSpecifier, currentFilePath) {
209
- if (moduleSpecifier.startsWith('./') || moduleSpecifier.startsWith('../')) {
210
- // Relative import - resolve relative to current file
211
- const currentDir = path.dirname(currentFilePath);
212
- return path.resolve(currentDir, moduleSpecifier + '.ts');
213
- }
214
- else {
215
- // Absolute import - assume it's in the project
216
- return path.resolve(process.cwd(), 'src', moduleSpecifier + '.ts');
217
- }
218
- }
219
- async function bundleTool(tool, distDir) {
220
- writeProgress(`📦 Bundling ${tool.className}...`);
221
- try {
222
- const outputPath = path.join(distDir, 'tools', `${tool.className}.js`);
223
- await build({
224
- entryPoints: [tool.filePath],
225
- bundle: true,
226
- format: 'cjs',
227
- platform: 'node',
228
- target: 'node16',
229
- outfile: outputPath,
230
- external: ['lua-cli/skill', 'lua-cli/user-data-api', 'lua-cli/product-api', 'lua-cli/custom-data-api', 'zod', 'keytar', 'esbuild', 'commander', 'inquirer', 'node-fetch', 'ws', 'socket.io-client', 'ts-morph'], // Exclude lua-cli modules, zod, and native modules
231
- minify: true, // Minify for smaller file sizes
232
- sourcemap: false,
233
- resolveExtensions: ['.ts', '.js', '.json'],
234
- define: {
235
- 'process.env.NODE_ENV': '"production"'
236
- },
237
- // Ensure all other dependencies are bundled
238
- packages: 'bundle',
239
- // Handle different module formats
240
- mainFields: ['main', 'module'],
241
- conditions: ['node']
242
- });
243
- // Add VM-compatible wrapper
244
- await wrapToolForVM(outputPath, tool);
245
- }
246
- catch (error) {
247
- console.warn(`Warning: Failed to bundle ${tool.className}:`, error);
248
- }
249
- }
250
- async function wrapToolForVM(outputPath, tool) {
251
- const bundledCode = fs.readFileSync(outputPath, 'utf8');
252
- // Create a wrapper that's compatible with the existing sandbox.ts VM system
253
- // The sandbox expects: const executeFunction = ${toolCode}; module.exports = async (input) => { return await executeFunction(input); };
254
- const wrappedCode = `async (input) => {
255
- // Mock lua-cli/skill module for external dependencies
256
- const luaCliSkill = {
257
- env: function(key) {
258
- if (typeof env !== 'undefined') {
259
- return env(key);
260
- }
261
- return process.env[key] || '';
262
- }
263
- };
264
-
265
- // Mock lua-cli/user-data-api module
266
- const luaCliUserDataApi = {
267
- user: typeof user !== 'undefined' ? user : {
268
- data: {
269
- get: async () => ({}),
270
- update: async (data) => data,
271
- create: async (data) => data
272
- }
273
- }
274
- };
275
-
276
- // Mock lua-cli/product-api module
277
- const luaCliProductApi = {
278
- product: typeof product !== 'undefined' ? product : {
279
- data: {
280
- get: async (page = 1, limit = 10) => ({ success: true, data: [], pagination: { currentPage: page, totalPages: 1, totalCount: 0, limit, hasNextPage: false, hasPrevPage: false, nextPage: null, prevPage: null } }),
281
- update: async (data, productId) => ({ updated: true, isNew: false, product: data }),
282
- create: async (data) => ({ updated: false, isNew: true, product: data }),
283
- delete: async (productId) => ({ deleted: true }),
284
- search: async (searchQuery) => ({ success: true, message: \`Found 0 products for "\${searchQuery}"\`, data: [] })
285
- }
286
- }
287
- };
288
-
289
- // Mock lua-cli/custom-data-api module
290
- const luaCliCustomDataApi = {
291
- customData: typeof customData !== 'undefined' ? customData : {
292
- create: async (collectionName, data) => ({
293
- id: 'mock-id-' + Date.now(),
294
- data: data.data,
295
- createdAt: Date.now(),
296
- updatedAt: Date.now(),
297
- searchText: data.searchText
298
- }),
299
- get: async (collectionName, filter, page = 1, limit = 10) => ({
300
- data: [],
301
- pagination: {
302
- currentPage: page,
303
- totalPages: 1,
304
- totalCount: 0,
305
- limit,
306
- hasNextPage: false,
307
- hasPrevPage: false
308
- }
309
- }),
310
- getEntry: async (collectionName, entryId) => null,
311
- update: async (collectionName, entryId, data) => ({
312
- status: 'success',
313
- message: 'Custom data entry updated'
314
- }),
315
- search: async (collectionName, searchText, limit = 10, scoreThreshold = 0.6) => ({
316
- data: [],
317
- count: 0
318
- }),
319
- delete: async (collectionName, entryId) => ({
320
- status: 'success',
321
- message: 'Custom data entry deleted'
322
- })
323
- }
324
- };
325
-
326
- // Mock zod module
327
- const zodModule = (() => {
328
- try {
329
- if (typeof require !== 'undefined') {
330
- return require('zod');
331
- }
332
- } catch (e) {
333
- // Fallback zod implementation
334
- }
335
- return {
336
- z: {
337
- object: (schema) => ({
338
- parse: (data) => data,
339
- safeParse: (data) => ({ success: true, data })
340
- }),
341
- string: () => ({
342
- parse: (data) => String(data),
343
- safeParse: (data) => ({ success: true, data: String(data) })
344
- }),
345
- number: () => ({
346
- parse: (data) => Number(data),
347
- safeParse: (data) => ({ success: true, data: Number(data) })
348
- }),
349
- boolean: () => ({
350
- parse: (data) => Boolean(data),
351
- safeParse: (data) => ({ success: true, data: Boolean(data) })
352
- })
353
- }
354
- };
355
- })();
356
-
357
- // Override require for external modules during execution
358
- const originalRequire = require;
359
- require = function(id) {
360
- if (id === 'lua-cli/skill') {
361
- return luaCliSkill;
362
- }
363
- if (id === 'lua-cli/user-data-api') {
364
- return luaCliUserDataApi;
365
- }
366
- if (id === 'lua-cli/product-api') {
367
- return luaCliProductApi;
368
- }
369
- if (id === 'lua-cli/custom-data-api') {
370
- return luaCliCustomDataApi;
371
- }
372
- if (id === 'zod') {
373
- return zodModule;
374
- }
375
- return originalRequire(id);
376
- };
377
-
378
- // Execute the bundled tool code
379
- ${bundledCode}
380
-
381
- // Restore original require
382
- require = originalRequire;
383
-
384
- // Get the tool class from exports
385
- const ToolClass = module.exports.default || module.exports.${tool.className} || module.exports;
386
-
387
- // Create and execute the tool
388
- const toolInstance = new ToolClass();
389
- return await toolInstance.execute(input);
390
- }`;
391
- fs.writeFileSync(outputPath, wrappedCode);
392
- }
393
- async function bundleMainIndex(indexPath, distDir) {
394
- writeProgress("📦 Bundling main index...");
395
- try {
396
- await build({
397
- entryPoints: [indexPath],
398
- bundle: true,
399
- format: 'cjs',
400
- platform: 'node',
401
- target: 'node16',
402
- outfile: path.join(distDir, 'index.js'),
403
- external: ['lua-cli/skill', 'lua-cli/user-data-api', 'lua-cli/product-api', 'lua-cli/custom-data-api', 'zod', 'keytar', 'esbuild', 'commander', 'inquirer', 'node-fetch', 'ws', 'socket.io-client', 'ts-morph'], // Exclude lua-cli modules, zod, and native modules
404
- minify: true, // Minify for smaller file sizes
405
- sourcemap: false,
406
- resolveExtensions: ['.ts', '.js', '.json'],
407
- define: {
408
- 'process.env.NODE_ENV': '"production"'
409
- },
410
- packages: 'bundle',
411
- mainFields: ['main', 'module'],
412
- conditions: ['node']
413
- });
414
- }
415
- catch (error) {
416
- console.warn("Warning: Failed to bundle main index:", error);
417
- }
418
- }
419
- async function extractExecuteCode(tool, project) {
420
- try {
421
- const toolSourceFile = project.getSourceFile(tool.filePath);
422
- if (!toolSourceFile) {
423
- console.warn(`Warning: Could not find source file for ${tool.className}`);
424
- return;
425
- }
426
- const classDecl = toolSourceFile.getClass(tool.className);
427
- if (!classDecl) {
428
- console.warn(`Warning: Could not find class ${tool.className} in ${tool.filePath}`);
429
- return;
430
- }
431
- // Extract the execute method
432
- const executeMethod = classDecl.getMethod('execute');
433
- if (!executeMethod) {
434
- console.warn(`Warning: Could not find execute method in ${tool.className}`);
435
- return;
436
- }
437
- // Get the execute method body
438
- const executeBody = executeMethod.getBodyText();
439
- if (!executeBody) {
440
- console.warn(`Warning: Execute method has no body in ${tool.className}`);
441
- return;
442
- }
443
- // Read the bundled tool file to get the self-contained code
444
- const bundledPath = path.join(process.cwd(), 'dist', 'tools', `${tool.className}.js`);
445
- if (fs.existsSync(bundledPath)) {
446
- const bundledCode = fs.readFileSync(bundledPath, 'utf8');
447
- // Extract just the tool execution function from the bundled code
448
- // The bundled code contains the VM wrapper, we need to extract the core functionality
449
- const executeFunction = createExecuteFunction(bundledCode, tool);
450
- tool.executeCode = executeFunction;
451
- }
452
- // Extract input schema from zod definition using proper library
453
- const inputSchemaProperty = classDecl.getProperty('inputSchema');
454
- if (inputSchemaProperty && inputSchemaProperty.getInitializer()) {
455
- const initializer = inputSchemaProperty.getInitializer();
456
- if (initializer) {
457
- try {
458
- // Evaluate the Zod schema to get the actual schema object
459
- const zodSchemaCode = initializer.getText();
460
- tool.inputSchema = await evaluateZodSchemaToJsonSchema(zodSchemaCode);
461
- }
462
- catch (error) {
463
- console.warn(`Warning: Could not parse Zod schema for ${tool.className}:`, error);
464
- tool.inputSchema = { type: "object" };
465
- }
466
- }
467
- }
468
- if (!tool.inputSchema) {
469
- tool.inputSchema = { type: "object" };
470
- }
471
- }
472
- catch (error) {
473
- console.warn(`Warning: Could not extract execute code for ${tool.className}:`, error);
474
- }
475
- }
476
- function createExecuteFunction(bundledCode, tool) {
477
- // The bundled code is already wrapped for VM execution by wrapToolForVM
478
- // Just return it directly since it's already an async function
479
- return bundledCode;
480
- }
481
- async function evaluateZodSchemaToJsonSchema(zodSchemaCode) {
482
- try {
483
- // Import zod and zod-to-json-schema dynamically
484
- const { z } = await import('zod');
485
- const { zodToJsonSchema } = await import('zod-to-json-schema');
486
- // Create a safe evaluation context using Function constructor
487
- const evalFunction = new Function('z', `return ${zodSchemaCode}`);
488
- const zodSchema = evalFunction(z);
489
- // Convert to JSON Schema using the library
490
- const jsonSchema = zodToJsonSchema(zodSchema, 'schema');
491
- // Extract just the core schema, removing JSON Schema references and definitions
492
- if (jsonSchema.$ref && jsonSchema.definitions && jsonSchema.definitions.schema) {
493
- // Return just the schema definition without the wrapper
494
- return jsonSchema.definitions.schema;
495
- }
496
- // Remove the top-level $schema and title properties that we don't need
497
- const { $schema, title, definitions, $ref, ...cleanSchema } = jsonSchema;
498
- return cleanSchema;
499
- }
500
- catch (error) {
501
- console.warn('Warning: Could not evaluate Zod schema, falling back to basic parsing:', error);
502
- // Fallback to basic parsing for simple cases
503
- if (zodSchemaCode.includes('z.object({')) {
504
- return { type: 'object' };
505
- }
506
- if (zodSchemaCode.includes('z.string()'))
507
- return { type: 'string' };
508
- if (zodSchemaCode.includes('z.number()'))
509
- return { type: 'number' };
510
- if (zodSchemaCode.includes('z.boolean()'))
511
- return { type: 'boolean' };
512
- return { type: 'object' };
513
- }
514
- }
515
- async function createDeploymentData(tools, distDir) {
516
- const config = readSkillConfig();
517
- const packageJsonPath = path.join(process.cwd(), "package.json");
518
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
519
- const deploymentData = {
520
- name: config?.skill?.name || packageJson.name || "lua-skill",
521
- version: config?.skill?.version || packageJson.version || "1.0.0",
522
- skillId: config?.skill?.skillId || "",
523
- description: config?.skill?.description || packageJson.description || "",
524
- context: config?.skill?.context || "",
525
- tools: tools.map(tool => ({
526
- name: tool.name,
527
- className: tool.className,
528
- description: tool.description || "",
529
- filePath: `tools/${tool.className}.js`
530
- }))
531
- };
532
- fs.writeFileSync(path.join(distDir, 'deployment.json'), JSON.stringify(deploymentData, null, 2));
533
- }
534
- async function createLegacyDeploymentData(tools, luaDir, indexFile) {
535
- const config = readSkillConfig();
536
- const packageJsonPath = path.join(process.cwd(), "package.json");
537
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
538
- // Extract skills metadata from index.ts
539
- const skillsMetadata = extractSkillsMetadata(indexFile);
540
- // Group tools by skill based on addTools calls
541
- const skillToTools = new Map();
542
- // Find addTools calls to associate tools with skills
543
- indexFile.forEachDescendant((node) => {
544
- if (Node.isCallExpression(node)) {
545
- const expression = node.getExpression();
546
- if (Node.isPropertyAccessExpression(expression) && expression.getName() === 'addTools') {
547
- const object = expression.getExpression();
548
- const objectName = object.getText();
549
- // Get the tools array
550
- const args = node.getArguments();
551
- if (args.length > 0 && Node.isArrayLiteralExpression(args[0])) {
552
- const toolsArray = args[0];
553
- const toolNames = toolsArray.getElements().map((element) => {
554
- if (Node.isNewExpression(element)) {
555
- return element.getExpression().getText();
556
- }
557
- return '';
558
- }).filter(name => name);
559
- skillToTools.set(objectName, toolNames);
560
- }
561
- }
562
- }
563
- });
564
- // Create skills array with their associated tools
565
- const skillsArray = skillsMetadata.map(skillMeta => {
566
- let skillTools = [];
567
- // First, check for tools from constructor
568
- if (skillMeta.constructorTools && skillMeta.constructorTools.length > 0) {
569
- skillTools = tools.filter(tool => {
570
- return skillMeta.constructorTools.includes(tool.className) ||
571
- skillMeta.constructorTools.includes(tool.name);
572
- });
573
- }
574
- else {
575
- // Find tools for this skill by matching variable names from addTools calls
576
- for (const [varName, toolNames] of skillToTools.entries()) {
577
- // Simple heuristic: if variable name contains skill name, associate tools
578
- if (varName.toLowerCase().includes(skillMeta.name.toLowerCase().replace('-skill', '')) ||
579
- varName.toLowerCase().includes(skillMeta.name.toLowerCase())) {
580
- skillTools = tools.filter(tool => {
581
- return toolNames.includes(tool.className) || toolNames.includes(tool.name);
582
- });
583
- break;
584
- }
585
- }
586
- }
587
- return {
588
- name: skillMeta.name,
589
- version: skillMeta.version,
590
- description: skillMeta.description,
591
- context: skillMeta.context,
592
- tools: skillTools.map(tool => ({
593
- name: tool.name,
594
- description: tool.description || "",
595
- inputSchema: tool.inputSchema || { type: "object" },
596
- execute: compressCode(tool.executeCode || "")
597
- }))
598
- };
599
- });
600
- // Match skills from code with skills in YAML and create missing ones
601
- const updatedSkillsArray = await ensureSkillsExistInYaml(skillsArray, config);
602
- const deployData = {
603
- skills: updatedSkillsArray
604
- };
605
- // Write legacy deploy.json to .lua directory
606
- fs.writeFileSync(path.join(luaDir, "deploy.json"), JSON.stringify(deployData, null, 2));
607
- // Write individual tool files to .lua directory (uncompressed)
608
- for (const tool of tools) {
609
- if (tool.executeCode) {
610
- const toolFilePath = path.join(luaDir, `${tool.name}.js`);
611
- fs.writeFileSync(toolFilePath, tool.executeCode);
612
- }
613
- }
614
- }
615
- function extractSkillsMetadata(indexFile) {
616
- const skills = [];
617
- // Find all LuaSkill constructor calls
618
- indexFile.forEachDescendant((node) => {
619
- if (Node.isNewExpression(node)) {
620
- const expression = node.getExpression();
621
- if (expression.getText() === 'LuaSkill') {
622
- const args = node.getArguments();
623
- if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
624
- const configObj = args[0];
625
- let skillName = '';
626
- let skillVersion = '';
627
- let description = '';
628
- let context = '';
629
- let constructorTools = [];
630
- // Extract properties
631
- configObj.getProperties().forEach((prop) => {
632
- if (Node.isPropertyAssignment(prop)) {
633
- const name = prop.getName();
634
- const value = prop.getInitializer();
635
- if (name === 'name' && value) {
636
- skillName = value.getText().replace(/['"]/g, '');
637
- }
638
- else if (name === 'version' && value) {
639
- skillVersion = value.getText().replace(/['"]/g, '');
640
- }
641
- else if (name === 'description' && value) {
642
- description = value.getText().replace(/['"]/g, '');
643
- }
644
- else if (name === 'context' && value) {
645
- context = value.getText().replace(/['"]/g, '');
646
- }
647
- else if (name === 'tools' && value && Node.isArrayLiteralExpression(value)) {
648
- // Extract tools from constructor array
649
- const toolsArray = value;
650
- constructorTools = toolsArray.getElements().map((element) => {
651
- if (Node.isNewExpression(element)) {
652
- return element.getExpression().getText();
653
- }
654
- return '';
655
- }).filter(name => name);
656
- }
657
- }
658
- });
659
- if (skillName) {
660
- skills.push({
661
- name: skillName,
662
- version: skillVersion || '1.0.0',
663
- description,
664
- context,
665
- constructorTools
666
- });
667
- }
668
- }
669
- }
670
- }
671
- });
672
- return skills;
673
- }
674
- async function ensureSkillsExistInYaml(skillsArray, config) {
675
- const updatedSkillsArray = [];
676
- let yamlUpdated = false;
677
- // Get existing skills from YAML
678
- const existingSkills = config?.skills || [];
679
- const existingSkillsMap = new Map();
680
- // Create map of existing skills
681
- existingSkills.forEach((skill) => {
682
- existingSkillsMap.set(skill.name, skill);
683
- });
684
- // Process each detected skill
685
- for (const skill of skillsArray) {
686
- const existingSkill = existingSkillsMap.get(skill.name);
687
- if (existingSkill && existingSkill.skillId && existingSkill.skillId !== '') {
688
- // Skill exists with valid skillId - use it
689
- // console.log(`Using existing skillId for ${skill.name}: ${existingSkill.skillId}`);
690
- updatedSkillsArray.push({
691
- ...skill,
692
- skillId: existingSkill.skillId
693
- });
694
- }
695
- else {
696
- // Skill doesn't exist or missing skillId - create it via API
697
- // console.log(`Creating new skill: ${skill.name}`);
698
- try {
699
- // Get API key and agent ID for skill creation
700
- const apiKey = await loadApiKey();
701
- if (!apiKey) {
702
- throw new Error("No API key found. Run 'lua auth configure' first.");
703
- }
704
- const agentId = config?.agent?.agentId;
705
- if (!agentId) {
706
- throw new Error("No agent ID found in lua.skill.yaml. Run 'lua init' first.");
707
- }
708
- // Call create skill API
709
- console.log(`Calling create skill API for: ${skill.name}`);
710
- const skillPayload = {
711
- name: skill.name,
712
- description: skill.description || `A Lua skill for ${skill.name}`,
713
- context: skill.context || ''
714
- };
715
- const result = await ApiService.Skill.createSkill(apiKey, agentId, skillPayload);
716
- // console.log(`Create skill API response:`, result);
717
- if (result.success && result.data && result.data.id) {
718
- const newSkillId = result.data.id; // API returns 'id', not 'skillId'
719
- // console.log(`✅ Created skill ${skill.name} with ID: ${newSkillId}`);
720
- updatedSkillsArray.push({
721
- ...skill,
722
- skillId: newSkillId
723
- });
724
- // Update YAML with new skill
725
- if (!existingSkill) {
726
- existingSkills.push({
727
- name: skill.name || '',
728
- version: skill.version || '1.0.0',
729
- skillId: newSkillId
730
- });
731
- yamlUpdated = true;
732
- }
733
- else {
734
- // Update existing skill with new skillId
735
- existingSkill.skillId = newSkillId;
736
- yamlUpdated = true;
737
- }
738
- }
739
- else {
740
- console.error(`❌ Failed to create skill ${skill.name}:`, result.error);
741
- throw new Error(result.error?.message || 'Failed to create skill - no ID returned from API');
742
- }
743
- }
744
- catch (error) {
745
- console.error(`❌ Failed to create skill ${skill.name}:`, error);
746
- throw error; // Don't use temp IDs, let the process fail properly
747
- }
748
- }
749
- }
750
- // Update YAML file if needed
751
- if (yamlUpdated) {
752
- await updateYamlWithSkills(existingSkills, config);
753
- }
754
- return updatedSkillsArray;
755
- }
756
- async function updateYamlWithSkills(skills, config) {
757
- // Clean skills array to ensure no undefined values
758
- const cleanedSkills = skills.map(skill => ({
759
- name: skill.name || '',
760
- version: skill.version || '1.0.0',
761
- skillId: skill.skillId || '' // Ensure skillId is never undefined
762
- }));
763
- // Update config with cleaned skills array
764
- const updatedConfig = {
765
- ...config,
766
- skills: cleanedSkills
767
- };
768
- // Write updated YAML
769
- const yamlPath = path.join(process.cwd(), 'lua.skill.yaml');
770
- const yamlContent = yaml.dump(updatedConfig, {
771
- indent: 2,
772
- lineWidth: -1,
773
- noRefs: true,
774
- replacer: (key, value) => {
775
- // Replace undefined values with empty strings
776
- return value === undefined ? '' : value;
777
- }
58
+ /**
59
+ * Prepares output directories for compilation.
60
+ * Removes old directories and creates fresh ones for dist and .lua outputs.
61
+ *
62
+ * @returns Object containing the dist and lua directory paths
63
+ */
64
+ function prepareOutputDirectories() {
65
+ const distDir = path.join(process.cwd(), COMPILE_DIRS.DIST);
66
+ const luaDir = path.join(process.cwd(), COMPILE_DIRS.LUA);
67
+ // Clean up old directories
68
+ if (fs.existsSync(distDir)) {
69
+ fs.rmSync(distDir, { recursive: true, force: true });
70
+ }
71
+ if (fs.existsSync(luaDir)) {
72
+ fs.rmSync(luaDir, { recursive: true, force: true });
73
+ }
74
+ // Create fresh directory structures
75
+ fs.mkdirSync(distDir, { recursive: true });
76
+ fs.mkdirSync(path.join(distDir, COMPILE_DIRS.TOOLS), { recursive: true });
77
+ fs.mkdirSync(luaDir, { recursive: true });
78
+ return { distDir, luaDir };
79
+ }
80
+ /**
81
+ * Creates and configures a TypeScript project for AST analysis.
82
+ *
83
+ * @returns Configured ts-morph Project instance
84
+ */
85
+ function createTypeScriptProject() {
86
+ return new Project({
87
+ tsConfigFilePath: path.join(process.cwd(), COMPILE_FILES.TSCONFIG_JSON),
778
88
  });
779
- fs.writeFileSync(yamlPath, yamlContent);
780
- console.log('Updated lua.skill.yaml with new skills');
781
89
  }