lua-cli 2.2.8-alpha.2 โ†’ 2.3.0-alpha.1

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 (193) 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 +14 -9
  165. package/package.json +7 -4
  166. package/template/QUICKSTART.md +299 -144
  167. package/template/README.md +928 -349
  168. package/template/TOOL_EXAMPLES.md +655 -0
  169. package/template/package-lock.json +5 -5
  170. package/template/package.json +1 -1
  171. package/template/src/index.ts +147 -207
  172. package/template/src/tools/BasketTool.ts +128 -0
  173. package/template/src/tools/CustomDataTool.ts +7 -13
  174. package/template/src/tools/OrderTool.ts +54 -0
  175. package/template/src/tools/PaymentTool.ts +1 -1
  176. package/template/src/tools/ProductsTool.ts +56 -118
  177. package/template/src/tools/UserDataTool.ts +4 -26
  178. package/dist/common/config.d.ts +0 -5
  179. package/dist/common/config.js +0 -5
  180. package/dist/custom-data-api.d.ts +0 -72
  181. package/dist/custom-data-api.js +0 -174
  182. package/dist/product-api.d.ts +0 -189
  183. package/dist/product-api.js +0 -141
  184. package/dist/services/api.d.ts +0 -549
  185. package/dist/services/api.js +0 -596
  186. package/dist/skill.d.ts +0 -50
  187. package/dist/types.d.ts +0 -1
  188. package/dist/types.js +0 -2
  189. package/dist/user-data-api.d.ts +0 -39
  190. package/dist/user-data-api.js +0 -50
  191. package/template/API.md +0 -604
  192. package/template/DEVELOPER.md +0 -771
  193. package/template/lua.skill.yaml +0 -7
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Dev Helper Utilities
3
+ * Small helper functions for dev mode
4
+ */
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import { DEV_DEFAULTS } from '../config/dev.constants.js';
8
+ /**
9
+ * Reads the version from config (handles both old and new formats).
10
+ *
11
+ * @param config - The skill configuration
12
+ * @returns The version string
13
+ */
14
+ export function readConfigVersion(config) {
15
+ let version = config?.skill?.version; // Legacy format
16
+ if (!version && config?.skills && config.skills.length > 0) {
17
+ version = config.skills[0].version; // New format - use first skill's version
18
+ }
19
+ return version || DEV_DEFAULTS.VERSION;
20
+ }
21
+ /**
22
+ * Reads the deploy.json file from .lua directory.
23
+ *
24
+ * @returns Parsed deploy data or null if not found
25
+ */
26
+ export function readDeployJson() {
27
+ const deployPath = path.join(process.cwd(), '.lua', 'deploy.json');
28
+ if (!fs.existsSync(deployPath)) {
29
+ return null;
30
+ }
31
+ const deployContent = fs.readFileSync(deployPath, 'utf8');
32
+ return JSON.parse(deployContent);
33
+ }
34
+ /**
35
+ * Extracts skill ID from config (handles both old and new formats).
36
+ * For multi-skill projects, returns the first skill's ID.
37
+ *
38
+ * @param config - The skill configuration
39
+ * @returns The skill ID or null if not found
40
+ */
41
+ export function extractSkillId(config) {
42
+ let skillId = config?.skill?.skillId; // Legacy format
43
+ if (!skillId && config?.skills && config.skills.length > 0) {
44
+ skillId = config.skills[0].skillId; // New format - use first skill
45
+ }
46
+ return skillId || null;
47
+ }
48
+ /**
49
+ * Validates that the configuration has required fields.
50
+ *
51
+ * @param config - The skill configuration to validate
52
+ * @throws Error if configuration is invalid
53
+ */
54
+ export function validateConfig(config) {
55
+ if (!config) {
56
+ throw new Error("โŒ No lua.skill.yaml found. Please run this command from a skill directory.");
57
+ }
58
+ }
59
+ /**
60
+ * Validates that deploy data has skills.
61
+ *
62
+ * @param deployData - The deploy data to validate
63
+ * @throws Error if deploy data is invalid
64
+ */
65
+ export function validateDeployData(deployData) {
66
+ if (!deployData) {
67
+ throw new Error("โŒ No deploy.json found. Compilation may have failed.");
68
+ }
69
+ if (!deployData.skills || !Array.isArray(deployData.skills) || deployData.skills.length === 0) {
70
+ throw new Error("โŒ No skills found in deploy.json. Compilation may have failed.");
71
+ }
72
+ }
73
+ /**
74
+ * Validates that config has required agent information.
75
+ *
76
+ * @param config - The skill configuration
77
+ * @throws Error if agent info is missing
78
+ */
79
+ export function validateAgentConfig(config) {
80
+ if (!config.agent?.agentId) {
81
+ throw new Error("โŒ Missing agentId in skill configuration");
82
+ }
83
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Dev Server Utilities
3
+ * Handles HTTP server, WebSocket connections, and all HTTP endpoints for dev mode
4
+ */
5
+ import { WebSocketServer } from 'ws';
6
+ /**
7
+ * Server instance with WebSocket and broadcast capabilities
8
+ */
9
+ export interface DevServer {
10
+ server: any;
11
+ wss: WebSocketServer;
12
+ broadcastLog: (message: string, subType?: 'info' | 'warn' | 'error' | 'debug') => void;
13
+ }
14
+ /**
15
+ * Creates and starts the dev web server with chat interface and tool testing.
16
+ *
17
+ * @param apiKey - User's API key
18
+ * @param agentId - Agent ID
19
+ * @param skillId - Skill ID
20
+ * @param sandboxId - Sandbox skill ID
21
+ * @param port - Port to run server on
22
+ * @returns Server instance with WebSocket and broadcast function
23
+ */
24
+ export declare function createChatServer(apiKey: string, agentId: string, skillId: string, sandboxId: string, port: number): DevServer;
@@ -0,0 +1,555 @@
1
+ /**
2
+ * Dev Server Utilities
3
+ * Handles HTTP server, WebSocket connections, and all HTTP endpoints for dev mode
4
+ */
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import { fileURLToPath } from 'url';
8
+ import { createServer } from 'http';
9
+ import { WebSocketServer } from 'ws';
10
+ import { readSkillConfig, updateSkillYamlPersona } from './files.js';
11
+ import { executeTool, createBroadcastConsole, loadEnvironmentVariables } from './sandbox.js';
12
+ import { getSandboxSkillId } from './sandbox-storage.js';
13
+ import { sendChatMessage } from './dev-api.js';
14
+ import { CORS_HEADERS, HTTP_STATUS, DEV_DEFAULTS } from '../config/dev.constants.js';
15
+ const __filename = fileURLToPath(import.meta.url);
16
+ const __dirname = path.dirname(__filename);
17
+ /**
18
+ * Reads the deploy.json file from .lua directory
19
+ *
20
+ * @returns Parsed deploy data or null if not found
21
+ */
22
+ function readDeployJson() {
23
+ const deployPath = path.join(process.cwd(), '.lua', 'deploy.json');
24
+ if (!fs.existsSync(deployPath)) {
25
+ return null;
26
+ }
27
+ const deployContent = fs.readFileSync(deployPath, 'utf8');
28
+ return JSON.parse(deployContent);
29
+ }
30
+ /**
31
+ * Creates and starts the dev web server with chat interface and tool testing.
32
+ *
33
+ * @param apiKey - User's API key
34
+ * @param agentId - Agent ID
35
+ * @param skillId - Skill ID
36
+ * @param sandboxId - Sandbox skill ID
37
+ * @param port - Port to run server on
38
+ * @returns Server instance with WebSocket and broadcast function
39
+ */
40
+ export function createChatServer(apiKey, agentId, skillId, sandboxId, port) {
41
+ const deployData = readDeployJson();
42
+ const server = createServer((req, res) => {
43
+ // Enable CORS
44
+ Object.entries(CORS_HEADERS).forEach(([key, value]) => {
45
+ res.setHeader(key, value);
46
+ });
47
+ if (req.method === 'OPTIONS') {
48
+ res.writeHead(HTTP_STATUS.OK);
49
+ res.end();
50
+ return;
51
+ }
52
+ // Route handlers
53
+ if (req.method === 'POST' && req.url === '/chat') {
54
+ handleChatEndpoint(req, res, apiKey, agentId, skillId, sandboxId, deployData);
55
+ return;
56
+ }
57
+ if (req.method === 'GET' && req.url === '/persona') {
58
+ handleGetPersonaEndpoint(req, res);
59
+ return;
60
+ }
61
+ if (req.method === 'POST' && req.url === '/persona') {
62
+ handlePostPersonaEndpoint(req, res);
63
+ return;
64
+ }
65
+ if (req.method === 'GET' && req.url === '/api/config') {
66
+ handleGetConfigEndpoint(req, res);
67
+ return;
68
+ }
69
+ if (req.method === 'GET' && req.url === '/api/skills') {
70
+ handleGetSkillsEndpoint(req, res);
71
+ return;
72
+ }
73
+ if (req.method === 'GET' && req.url === '/env') {
74
+ handleGetEnvEndpoint(req, res);
75
+ return;
76
+ }
77
+ if (req.method === 'POST' && req.url === '/env') {
78
+ handlePostEnvEndpoint(req, res);
79
+ return;
80
+ }
81
+ if (req.method === 'GET' && req.url === '/tools') {
82
+ handleGetToolsEndpoint(req, res);
83
+ return;
84
+ }
85
+ if (req.method === 'POST' && req.url === '/tools/test') {
86
+ handleToolTestEndpoint(req, res, apiKey, agentId, wss);
87
+ return;
88
+ }
89
+ if (req.method === 'GET' && req.url === '/') {
90
+ handleIndexEndpoint(req, res, apiKey, agentId, skillId, sandboxId);
91
+ return;
92
+ }
93
+ if (req.method === 'GET' && req.url === '/app.js') {
94
+ handleStaticFile(req, res, 'app.js', 'application/javascript');
95
+ return;
96
+ }
97
+ if (req.method === 'GET' && req.url === '/app.css') {
98
+ handleStaticFile(req, res, 'app.css', 'text/css');
99
+ return;
100
+ }
101
+ if (req.method === 'GET' && req.url === '/tools-page.css') {
102
+ handleStaticFile(req, res, 'tools-page.css', 'text/css');
103
+ return;
104
+ }
105
+ res.writeHead(HTTP_STATUS.NOT_FOUND);
106
+ res.end('Not Found');
107
+ });
108
+ // Create WebSocket server for log streaming
109
+ const wss = new WebSocketServer({ server });
110
+ wss.on('connection', (ws) => {
111
+ ws.on('close', () => {
112
+ // Client disconnected
113
+ });
114
+ ws.on('error', (error) => {
115
+ console.error('WebSocket error:', error);
116
+ });
117
+ // Send initial connection message
118
+ ws.send(JSON.stringify({
119
+ type: 'log',
120
+ subType: 'info',
121
+ message: 'Connected to CLI dev server',
122
+ timestamp: new Date().toISOString()
123
+ }));
124
+ });
125
+ server.listen(port, () => {
126
+ // Server started
127
+ });
128
+ // Function to broadcast logs to all connected clients
129
+ const broadcastLog = (message, subType = 'info') => {
130
+ const logMessage = {
131
+ type: 'log',
132
+ subType,
133
+ message,
134
+ timestamp: new Date().toISOString(),
135
+ id: Date.now().toString()
136
+ };
137
+ wss.clients.forEach((client) => {
138
+ if (client.readyState === 1) { // WebSocket.OPEN
139
+ client.send(JSON.stringify(logMessage));
140
+ }
141
+ });
142
+ };
143
+ return { server, wss, broadcastLog };
144
+ }
145
+ // ============================================================================
146
+ // ENDPOINT HANDLERS
147
+ // ============================================================================
148
+ /**
149
+ * Handles POST /chat - Send chat messages
150
+ */
151
+ function handleChatEndpoint(req, res, apiKey, agentId, skillId, sandboxId, deployData) {
152
+ let body = '';
153
+ req.on('data', (chunk) => {
154
+ body += chunk.toString();
155
+ });
156
+ req.on('end', async () => {
157
+ try {
158
+ const { message, persona } = JSON.parse(body);
159
+ const response = await sendChatMessage(apiKey, agentId, skillId, sandboxId, message, persona, deployData);
160
+ res.writeHead(HTTP_STATUS.OK, { 'Content-Type': 'application/json' });
161
+ res.end(JSON.stringify({
162
+ success: response !== null,
163
+ text: response || 'Error sending message',
164
+ error: response === null ? 'Failed to send message' : null
165
+ }));
166
+ }
167
+ catch (error) {
168
+ res.writeHead(HTTP_STATUS.SERVER_ERROR, { 'Content-Type': 'application/json' });
169
+ res.end(JSON.stringify({ success: false, error: 'Invalid request' }));
170
+ }
171
+ });
172
+ }
173
+ /**
174
+ * Handles GET /persona - Get current persona
175
+ */
176
+ function handleGetPersonaEndpoint(req, res) {
177
+ try {
178
+ const config = readSkillConfig();
179
+ const persona = config?.agent?.persona || '';
180
+ res.writeHead(HTTP_STATUS.OK, { 'Content-Type': 'application/json' });
181
+ res.end(JSON.stringify({
182
+ success: true,
183
+ persona: persona
184
+ }));
185
+ }
186
+ catch (error) {
187
+ res.writeHead(HTTP_STATUS.SERVER_ERROR, { 'Content-Type': 'application/json' });
188
+ res.end(JSON.stringify({ success: false, error: 'Failed to read persona' }));
189
+ }
190
+ }
191
+ /**
192
+ * Handles POST /persona - Update persona
193
+ */
194
+ function handlePostPersonaEndpoint(req, res) {
195
+ let body = '';
196
+ req.on('data', (chunk) => {
197
+ body += chunk.toString();
198
+ });
199
+ req.on('end', async () => {
200
+ try {
201
+ const { persona } = JSON.parse(body);
202
+ updateSkillYamlPersona(persona);
203
+ res.writeHead(HTTP_STATUS.OK, { 'Content-Type': 'application/json' });
204
+ res.end(JSON.stringify({
205
+ success: true,
206
+ message: 'Persona updated successfully'
207
+ }));
208
+ }
209
+ catch (error) {
210
+ res.writeHead(HTTP_STATUS.SERVER_ERROR, { 'Content-Type': 'application/json' });
211
+ res.end(JSON.stringify({ success: false, error: 'Failed to update persona' }));
212
+ }
213
+ });
214
+ }
215
+ /**
216
+ * Handles GET /api/config - Get agent configuration
217
+ */
218
+ function handleGetConfigEndpoint(req, res) {
219
+ try {
220
+ const config = readSkillConfig();
221
+ res.writeHead(HTTP_STATUS.OK, { 'Content-Type': 'application/json' });
222
+ res.end(JSON.stringify({
223
+ agent: {
224
+ agentId: config?.agent?.agentId || '',
225
+ orgId: config?.agent?.orgId || ''
226
+ }
227
+ }));
228
+ }
229
+ catch (error) {
230
+ console.error('Error reading config:', error);
231
+ res.writeHead(HTTP_STATUS.SERVER_ERROR, { 'Content-Type': 'application/json' });
232
+ res.end(JSON.stringify({ error: 'Failed to read configuration' }));
233
+ }
234
+ }
235
+ /**
236
+ * Handles GET /api/skills - Get all skills information
237
+ */
238
+ function handleGetSkillsEndpoint(req, res) {
239
+ (async () => {
240
+ try {
241
+ const skillsData = [];
242
+ // First try to read from deploy.json in the .lua directory
243
+ const deployPath = path.join(process.cwd(), '.lua', 'deploy.json');
244
+ if (fs.existsSync(deployPath)) {
245
+ // Read from compiled deploy.json
246
+ const deployData = JSON.parse(fs.readFileSync(deployPath, 'utf8'));
247
+ if (deployData.skills && Array.isArray(deployData.skills)) {
248
+ // New format with skills array
249
+ for (const skill of deployData.skills) {
250
+ const sandboxId = await getSandboxSkillId(skill.name);
251
+ skillsData.push({
252
+ name: skill.name || DEV_DEFAULTS.SKILL_NAME,
253
+ description: skill.description || DEV_DEFAULTS.DESCRIPTION,
254
+ context: skill.context || '',
255
+ version: skill.version || DEV_DEFAULTS.VERSION,
256
+ skillId: skill.skillId || '',
257
+ sandboxId: sandboxId || '',
258
+ tools: skill.tools ? skill.tools.map((tool) => tool.name) : []
259
+ });
260
+ }
261
+ }
262
+ else {
263
+ // Legacy format fallback
264
+ const toolNames = Object.keys(deployData.tools || {});
265
+ const sandboxId = await getSandboxSkillId(); // Legacy uses no skill name
266
+ skillsData.push({
267
+ name: deployData.name || 'Current Skill',
268
+ description: deployData.description || DEV_DEFAULTS.DESCRIPTION,
269
+ context: deployData.context || '',
270
+ version: deployData.version || DEV_DEFAULTS.VERSION,
271
+ skillId: deployData.skillId || '',
272
+ sandboxId: sandboxId || '',
273
+ tools: toolNames
274
+ });
275
+ }
276
+ }
277
+ else {
278
+ // Fallback: Read from YAML and scan tools directory
279
+ const config = readSkillConfig();
280
+ const toolsDir = path.join(process.cwd(), 'src', 'tools');
281
+ let toolNames = [];
282
+ if (fs.existsSync(toolsDir)) {
283
+ const toolFiles = fs.readdirSync(toolsDir)
284
+ .filter((file) => file.endsWith('.ts'))
285
+ .map((file) => file.replace('.ts', ''));
286
+ toolNames = toolFiles;
287
+ }
288
+ if (config?.skills && Array.isArray(config.skills)) {
289
+ // New format: multiple skills
290
+ for (const skill of config.skills) {
291
+ const sandboxId = await getSandboxSkillId(skill.name);
292
+ skillsData.push({
293
+ name: skill.name || DEV_DEFAULTS.SKILL_NAME,
294
+ description: DEV_DEFAULTS.NOT_COMPILED_DESCRIPTION,
295
+ context: DEV_DEFAULTS.NOT_COMPILED_CONTEXT,
296
+ version: skill.version || DEV_DEFAULTS.VERSION,
297
+ skillId: skill.skillId || '',
298
+ sandboxId: sandboxId || '',
299
+ tools: toolNames // All tools for now since not compiled
300
+ });
301
+ }
302
+ }
303
+ else if (config?.skill) {
304
+ // Legacy format: single skill
305
+ const sandboxId = await getSandboxSkillId(); // Legacy uses no skill name
306
+ skillsData.push({
307
+ name: config.skill.name || 'Current Skill',
308
+ description: DEV_DEFAULTS.NOT_COMPILED_DESCRIPTION,
309
+ context: DEV_DEFAULTS.NOT_COMPILED_CONTEXT,
310
+ version: config.skill.version || DEV_DEFAULTS.VERSION,
311
+ skillId: config.skill.skillId || '',
312
+ sandboxId: sandboxId || '',
313
+ tools: toolNames
314
+ });
315
+ }
316
+ }
317
+ res.writeHead(HTTP_STATUS.OK, { 'Content-Type': 'application/json' });
318
+ res.end(JSON.stringify({
319
+ success: true,
320
+ skills: skillsData
321
+ }));
322
+ }
323
+ catch (error) {
324
+ console.error('Error reading skills:', error);
325
+ res.writeHead(HTTP_STATUS.SERVER_ERROR, { 'Content-Type': 'application/json' });
326
+ res.end(JSON.stringify({
327
+ success: false,
328
+ error: 'Failed to read skills configuration'
329
+ }));
330
+ }
331
+ })();
332
+ }
333
+ /**
334
+ * Handles GET /env - Get environment variables
335
+ */
336
+ function handleGetEnvEndpoint(req, res) {
337
+ try {
338
+ const envPath = path.join(process.cwd(), '.env');
339
+ let envContent = '';
340
+ if (fs.existsSync(envPath)) {
341
+ envContent = fs.readFileSync(envPath, 'utf8');
342
+ }
343
+ res.writeHead(HTTP_STATUS.OK, { 'Content-Type': 'application/json' });
344
+ res.end(JSON.stringify({
345
+ success: true,
346
+ content: envContent
347
+ }));
348
+ }
349
+ catch (error) {
350
+ res.writeHead(HTTP_STATUS.SERVER_ERROR, { 'Content-Type': 'application/json' });
351
+ res.end(JSON.stringify({ success: false, error: 'Failed to read .env file' }));
352
+ }
353
+ }
354
+ /**
355
+ * Handles POST /env - Update environment variables
356
+ */
357
+ function handlePostEnvEndpoint(req, res) {
358
+ let body = '';
359
+ req.on('data', (chunk) => {
360
+ body += chunk.toString();
361
+ });
362
+ req.on('end', async () => {
363
+ try {
364
+ const { content } = JSON.parse(body);
365
+ const envPath = path.join(process.cwd(), '.env');
366
+ // Write the .env file
367
+ fs.writeFileSync(envPath, content || '', 'utf8');
368
+ res.writeHead(HTTP_STATUS.OK, { 'Content-Type': 'application/json' });
369
+ res.end(JSON.stringify({
370
+ success: true,
371
+ message: 'Environment variables updated successfully'
372
+ }));
373
+ }
374
+ catch (error) {
375
+ res.writeHead(HTTP_STATUS.SERVER_ERROR, { 'Content-Type': 'application/json' });
376
+ res.end(JSON.stringify({ success: false, error: 'Failed to update .env file' }));
377
+ }
378
+ });
379
+ }
380
+ /**
381
+ * Handles GET /tools - Get all tools
382
+ */
383
+ function handleGetToolsEndpoint(req, res) {
384
+ try {
385
+ // Read the deploy.json file to get tools
386
+ const deployPath = path.join(process.cwd(), '.lua', 'deploy.json');
387
+ if (fs.existsSync(deployPath)) {
388
+ const deployData = JSON.parse(fs.readFileSync(deployPath, 'utf8'));
389
+ // Handle both new skills array format and legacy tools format
390
+ let tools = deployData.tools || {};
391
+ if (deployData.skills && Array.isArray(deployData.skills)) {
392
+ // New format: merge all tools from all skills
393
+ tools = {};
394
+ deployData.skills.forEach((skill) => {
395
+ if (skill.tools && Array.isArray(skill.tools)) {
396
+ skill.tools.forEach((tool) => {
397
+ tools[tool.name] = tool;
398
+ });
399
+ }
400
+ });
401
+ }
402
+ res.writeHead(HTTP_STATUS.OK, { 'Content-Type': 'application/json' });
403
+ res.end(JSON.stringify({
404
+ success: true,
405
+ tools: tools
406
+ }));
407
+ }
408
+ else {
409
+ res.writeHead(HTTP_STATUS.NOT_FOUND, { 'Content-Type': 'application/json' });
410
+ res.end(JSON.stringify({ success: false, error: 'deploy.json not found' }));
411
+ }
412
+ }
413
+ catch (error) {
414
+ res.writeHead(HTTP_STATUS.SERVER_ERROR, { 'Content-Type': 'application/json' });
415
+ res.end(JSON.stringify({ success: false, error: 'Failed to load tools' }));
416
+ }
417
+ }
418
+ /**
419
+ * Handles POST /tools/test - Test a tool
420
+ */
421
+ function handleToolTestEndpoint(req, res, apiKey, agentId, wss) {
422
+ let body = '';
423
+ req.on('data', (chunk) => {
424
+ body += chunk.toString();
425
+ });
426
+ req.on('end', async () => {
427
+ try {
428
+ const { toolName, inputs } = JSON.parse(body);
429
+ // Read deploy.json to get the tool's execute function
430
+ const deployPath = path.join(process.cwd(), '.lua', 'deploy.json');
431
+ if (!fs.existsSync(deployPath)) {
432
+ res.writeHead(HTTP_STATUS.NOT_FOUND, { 'Content-Type': 'application/json' });
433
+ res.end(JSON.stringify({ success: false, error: 'deploy.json not found' }));
434
+ return;
435
+ }
436
+ const deployData = JSON.parse(fs.readFileSync(deployPath, 'utf8'));
437
+ let tool = null;
438
+ if (deployData.skills && Array.isArray(deployData.skills)) {
439
+ // New format: search through skills array
440
+ for (const skill of deployData.skills) {
441
+ if (skill.tools && Array.isArray(skill.tools)) {
442
+ tool = skill.tools.find((t) => t.name === toolName);
443
+ if (tool)
444
+ break;
445
+ }
446
+ }
447
+ }
448
+ else {
449
+ // Legacy format: search in tools object
450
+ if (deployData.tools && deployData.tools[toolName]) {
451
+ tool = {
452
+ name: toolName,
453
+ ...deployData.tools[toolName]
454
+ };
455
+ }
456
+ }
457
+ if (!tool) {
458
+ res.writeHead(HTTP_STATUS.NOT_FOUND, { 'Content-Type': 'application/json' });
459
+ res.end(JSON.stringify({ success: false, error: 'Tool not found' }));
460
+ return;
461
+ }
462
+ // Load environment variables from all sources (.env + yaml)
463
+ const envVars = loadEnvironmentVariables();
464
+ // Decompress and execute the tool
465
+ const { gunzipSync } = await import('zlib');
466
+ const { Buffer } = await import('buffer');
467
+ function decompressCode(compressedCode) {
468
+ const buffer = Buffer.from(compressedCode, 'base64');
469
+ return gunzipSync(buffer).toString('utf8');
470
+ }
471
+ const toolCode = decompressCode(tool.execute);
472
+ // Broadcast tool test start
473
+ wss.clients.forEach((client) => {
474
+ if (client.readyState === 1) {
475
+ client.send(JSON.stringify({
476
+ type: 'log',
477
+ subType: 'info',
478
+ message: `๐Ÿงช Testing tool: ${toolName}`,
479
+ timestamp: new Date().toISOString(),
480
+ id: Date.now().toString()
481
+ }));
482
+ }
483
+ });
484
+ // Create custom console that captures output and sends via WebSocket
485
+ const customConsole = createBroadcastConsole((logData) => {
486
+ wss.clients.forEach((client) => {
487
+ if (client.readyState === 1) { // WebSocket.OPEN
488
+ client.send(JSON.stringify(logData));
489
+ }
490
+ });
491
+ });
492
+ // Execute the tool using the common utility
493
+ const result = await executeTool({
494
+ toolCode,
495
+ inputs,
496
+ apiKey,
497
+ agentId,
498
+ customConsole
499
+ });
500
+ // Broadcast tool test completion
501
+ wss.clients.forEach((client) => {
502
+ if (client.readyState === 1) {
503
+ client.send(JSON.stringify({
504
+ type: 'log',
505
+ subType: 'info',
506
+ message: `โœ… Tool test completed: ${toolName}`,
507
+ timestamp: new Date().toISOString(),
508
+ id: (Date.now() + 1).toString()
509
+ }));
510
+ }
511
+ });
512
+ res.writeHead(HTTP_STATUS.OK, { 'Content-Type': 'application/json' });
513
+ res.end(JSON.stringify({
514
+ success: true,
515
+ result: result
516
+ }));
517
+ }
518
+ catch (error) {
519
+ res.writeHead(HTTP_STATUS.SERVER_ERROR, { 'Content-Type': 'application/json' });
520
+ res.end(JSON.stringify({
521
+ success: false,
522
+ error: error.message || 'Tool execution failed'
523
+ }));
524
+ }
525
+ });
526
+ }
527
+ /**
528
+ * Handles GET / - Serve main HTML page
529
+ */
530
+ function handleIndexEndpoint(req, res, apiKey, agentId, skillId, sandboxId) {
531
+ // Read the React app HTML template from dist directory
532
+ const htmlTemplate = fs.readFileSync(path.join(__dirname, '..', 'web', 'index.html'), 'utf8');
533
+ // Replace placeholders with actual values
534
+ const html = htmlTemplate
535
+ .replace('{{API_KEY}}', apiKey)
536
+ .replace('{{AGENT_ID}}', agentId)
537
+ .replace('{{SKILL_ID}}', skillId)
538
+ .replace('{{SANDBOX_ID}}', sandboxId);
539
+ res.writeHead(HTTP_STATUS.OK, { 'Content-Type': 'text/html' });
540
+ res.end(html);
541
+ }
542
+ /**
543
+ * Handles static file serving (JS, CSS)
544
+ */
545
+ function handleStaticFile(req, res, filename, contentType) {
546
+ try {
547
+ const content = fs.readFileSync(path.join(__dirname, '..', 'web', filename), 'utf8');
548
+ res.writeHead(HTTP_STATUS.OK, { 'Content-Type': contentType });
549
+ res.end(content);
550
+ }
551
+ catch (error) {
552
+ res.writeHead(HTTP_STATUS.NOT_FOUND, { 'Content-Type': 'text/plain' });
553
+ res.end(`${filename} not found`);
554
+ }
555
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Dev File Watcher Utilities
3
+ * Handles file system watching and automatic recompilation
4
+ */
5
+ /**
6
+ * Configuration for file watcher
7
+ */
8
+ export interface FileWatcherConfig {
9
+ apiKey: string;
10
+ agentId: string;
11
+ readDeployJson: () => any | null;
12
+ broadcastLog: (message: string, subType?: 'info' | 'warn' | 'error' | 'debug') => void;
13
+ }
14
+ /**
15
+ * Checks if a file should be ignored during watching.
16
+ *
17
+ * @param filename - The filename to check
18
+ * @returns True if file should be ignored, false otherwise
19
+ */
20
+ export declare function shouldIgnoreFile(filename: string | null): boolean;
21
+ /**
22
+ * Creates and starts a file watcher for development mode.
23
+ * Watches for file changes and automatically recompiles and pushes to sandbox.
24
+ *
25
+ * @param config - File watcher configuration
26
+ * @returns The watcher instance
27
+ */
28
+ export declare function createFileWatcher(config: FileWatcherConfig): {
29
+ watcher: import("fs").FSWatcher;
30
+ cleanup: () => void;
31
+ };