lua-cli 1.2.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.
- package/CHANGELOG.md +50 -0
- package/README.md +244 -63
- package/dist/commands/agents.js +17 -17
- package/dist/commands/apiKey.js +24 -19
- package/dist/commands/compile.d.ts +1 -0
- package/dist/commands/compile.js +1004 -0
- package/dist/commands/configure.js +93 -68
- package/dist/commands/deploy-new.d.ts +0 -0
- package/dist/commands/deploy-new.js +130 -0
- package/dist/commands/deploy.d.ts +19 -0
- package/dist/commands/deploy.js +81 -763
- package/dist/commands/destroy.js +26 -21
- package/dist/commands/dev.d.ts +63 -0
- package/dist/commands/dev.js +656 -0
- package/dist/commands/index.d.ts +4 -2
- package/dist/commands/index.js +4 -2
- package/dist/commands/init.js +297 -62
- package/dist/commands/push.d.ts +22 -0
- package/dist/commands/push.js +127 -0
- package/dist/commands/test.js +14 -15
- package/dist/index.d.ts +1 -1
- package/dist/index.js +35 -19
- package/dist/services/api.d.ts +195 -0
- package/dist/services/api.js +209 -0
- package/dist/services/auth.d.ts +102 -0
- package/dist/services/auth.js +129 -40
- package/dist/skill.d.ts +22 -1
- package/dist/skill.js +21 -1
- package/dist/types/index.d.ts +16 -2
- package/dist/types/index.js +16 -1
- package/dist/user-data-api.d.ts +52 -0
- package/dist/user-data-api.js +151 -0
- package/dist/utils/cli.d.ts +34 -0
- package/dist/utils/cli.js +58 -0
- package/dist/utils/files.d.ts +4 -1
- package/dist/utils/files.js +61 -14
- package/dist/web/app.css +1050 -0
- package/dist/web/app.js +79 -0
- package/dist/web/tools-page.css +377 -0
- package/package.json +18 -5
- package/template/package-lock.json +32 -3
- package/template/package.json +3 -1
- package/template/{index.ts → src/index.ts} +13 -4
- package/template/src/tools/UserPreferencesTool.ts +73 -0
- package/template/tools/UserPreferencesTool.ts +73 -0
- package/template/tsconfig.json +1 -1
- package/template/.lua/deploy.json +0 -145
- /package/template/{services → src/services}/ApiService.ts +0 -0
- /package/template/{services → src/services}/GetWeather.ts +0 -0
- /package/template/{services → src/services}/MathService.ts +0 -0
- /package/template/{tools → src/tools}/AdvancedMathTool.ts +0 -0
- /package/template/{tools → src/tools}/CalculatorTool.ts +0 -0
- /package/template/{tools → src/tools}/CreatePostTool.ts +0 -0
- /package/template/{tools → src/tools}/GetUserDataTool.ts +0 -0
- /package/template/{tools → src/tools}/GetWeatherTool.ts +0 -0
|
@@ -0,0 +1,656 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import inquirer from 'inquirer';
|
|
5
|
+
import { compileCommand } from './compile.js';
|
|
6
|
+
import { checkApiKey, loadApiKey } from '../services/auth.js';
|
|
7
|
+
import { ApiService } from '../services/api.js';
|
|
8
|
+
import { readSkillConfig, updateSkillYamlPersona } from '../utils/files.js';
|
|
9
|
+
import { withErrorHandling, clearPromptLines, writeProgress, writeSuccess } from '../utils/cli.js';
|
|
10
|
+
import keytar from 'keytar';
|
|
11
|
+
import { watch } from 'fs';
|
|
12
|
+
import { createServer } from 'http';
|
|
13
|
+
import { WebSocketServer } from 'ws';
|
|
14
|
+
import open from 'open';
|
|
15
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
+
const __dirname = path.dirname(__filename);
|
|
17
|
+
// Constants for sandbox skill ID storage
|
|
18
|
+
const SANDBOX_SERVICE = "lua-cli-sandbox";
|
|
19
|
+
const SANDBOX_ACCOUNT = "sandbox-skill-id";
|
|
20
|
+
// Function to get sandbox skill ID from secure storage
|
|
21
|
+
async function getSandboxSkillId() {
|
|
22
|
+
try {
|
|
23
|
+
return await keytar.getPassword(SANDBOX_SERVICE, SANDBOX_ACCOUNT);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// Function to store sandbox skill ID in secure storage
|
|
30
|
+
async function setSandboxSkillId(skillId) {
|
|
31
|
+
try {
|
|
32
|
+
await keytar.setPassword(SANDBOX_SERVICE, SANDBOX_ACCOUNT, skillId);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
// Ignore storage errors
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Function to send chat message to API
|
|
39
|
+
export async function sendChatMessage(apiKey, agentId, skillId, sandboxId, message, persona) {
|
|
40
|
+
try {
|
|
41
|
+
const chatRequest = {
|
|
42
|
+
messages: [
|
|
43
|
+
{
|
|
44
|
+
type: "text",
|
|
45
|
+
text: message
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
navigate: true,
|
|
49
|
+
skillOverride: [
|
|
50
|
+
{
|
|
51
|
+
skillId: skillId,
|
|
52
|
+
sandboxId: sandboxId
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
};
|
|
56
|
+
// Add persona override if provided
|
|
57
|
+
if (persona) {
|
|
58
|
+
chatRequest.personaOverride = persona;
|
|
59
|
+
}
|
|
60
|
+
const response = await ApiService.Chat.sendMessage(agentId, chatRequest, apiKey);
|
|
61
|
+
if (!response.success) {
|
|
62
|
+
console.error(`❌ Chat API error: ${response.error?.message || 'Unknown error'}`);
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
return response.data?.text || null;
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
console.error("❌ Error sending chat message:", error);
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Function to create and start web server for chat interface
|
|
73
|
+
function createChatServer(apiKey, agentId, skillId, sandboxId, port = 3000) {
|
|
74
|
+
const server = createServer((req, res) => {
|
|
75
|
+
// Enable CORS
|
|
76
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
77
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
78
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
79
|
+
if (req.method === 'OPTIONS') {
|
|
80
|
+
res.writeHead(200);
|
|
81
|
+
res.end();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (req.method === 'POST' && req.url === '/chat') {
|
|
85
|
+
let body = '';
|
|
86
|
+
req.on('data', chunk => {
|
|
87
|
+
body += chunk.toString();
|
|
88
|
+
});
|
|
89
|
+
req.on('end', async () => {
|
|
90
|
+
try {
|
|
91
|
+
const { message, persona } = JSON.parse(body);
|
|
92
|
+
const response = await sendChatMessage(apiKey, agentId, skillId, sandboxId, message, persona);
|
|
93
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
94
|
+
res.end(JSON.stringify({
|
|
95
|
+
success: response !== null,
|
|
96
|
+
text: response || 'Error sending message',
|
|
97
|
+
error: response === null ? 'Failed to send message' : null
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
102
|
+
res.end(JSON.stringify({ success: false, error: 'Invalid request' }));
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
// Persona management endpoints
|
|
108
|
+
if (req.method === 'GET' && req.url === '/persona') {
|
|
109
|
+
try {
|
|
110
|
+
const config = readSkillConfig();
|
|
111
|
+
const persona = config?.agent?.persona || '';
|
|
112
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
113
|
+
res.end(JSON.stringify({
|
|
114
|
+
success: true,
|
|
115
|
+
persona: persona
|
|
116
|
+
}));
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
120
|
+
res.end(JSON.stringify({ success: false, error: 'Failed to read persona' }));
|
|
121
|
+
}
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (req.method === 'POST' && req.url === '/persona') {
|
|
125
|
+
let body = '';
|
|
126
|
+
req.on('data', chunk => {
|
|
127
|
+
body += chunk.toString();
|
|
128
|
+
});
|
|
129
|
+
req.on('end', async () => {
|
|
130
|
+
try {
|
|
131
|
+
const { persona } = JSON.parse(body);
|
|
132
|
+
// Update the YAML file
|
|
133
|
+
updateSkillYamlPersona(persona);
|
|
134
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
135
|
+
res.end(JSON.stringify({
|
|
136
|
+
success: true,
|
|
137
|
+
message: 'Persona updated successfully'
|
|
138
|
+
}));
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
142
|
+
res.end(JSON.stringify({ success: false, error: 'Failed to update persona' }));
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
// Tools endpoints
|
|
148
|
+
if (req.method === 'GET' && req.url === '/tools') {
|
|
149
|
+
try {
|
|
150
|
+
// Read the deploy.json file to get tools
|
|
151
|
+
const deployPath = path.join(process.cwd(), '.lua', 'deploy.json');
|
|
152
|
+
if (fs.existsSync(deployPath)) {
|
|
153
|
+
const deployData = JSON.parse(fs.readFileSync(deployPath, 'utf8'));
|
|
154
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
155
|
+
res.end(JSON.stringify({
|
|
156
|
+
success: true,
|
|
157
|
+
tools: deployData.tools || []
|
|
158
|
+
}));
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
162
|
+
res.end(JSON.stringify({ success: false, error: 'deploy.json not found' }));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
167
|
+
res.end(JSON.stringify({ success: false, error: 'Failed to load tools' }));
|
|
168
|
+
}
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (req.method === 'POST' && req.url === '/tools/test') {
|
|
172
|
+
let body = '';
|
|
173
|
+
req.on('data', chunk => {
|
|
174
|
+
body += chunk.toString();
|
|
175
|
+
});
|
|
176
|
+
req.on('end', async () => {
|
|
177
|
+
try {
|
|
178
|
+
const { toolName, inputs } = JSON.parse(body);
|
|
179
|
+
// Read deploy.json to get the tool's execute function
|
|
180
|
+
const deployPath = path.join(process.cwd(), '.lua', 'deploy.json');
|
|
181
|
+
if (!fs.existsSync(deployPath)) {
|
|
182
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
183
|
+
res.end(JSON.stringify({ success: false, error: 'deploy.json not found' }));
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const deployData = JSON.parse(fs.readFileSync(deployPath, 'utf8'));
|
|
187
|
+
const tool = deployData.tools.find((t) => t.name === toolName);
|
|
188
|
+
if (!tool) {
|
|
189
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
190
|
+
res.end(JSON.stringify({ success: false, error: 'Tool not found' }));
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
// Decompress and execute the tool
|
|
194
|
+
const { gunzipSync } = await import('zlib');
|
|
195
|
+
const { Buffer } = await import('buffer');
|
|
196
|
+
const { createRequire } = await import('module');
|
|
197
|
+
const vm = await import('vm');
|
|
198
|
+
function decompressCode(compressedCode) {
|
|
199
|
+
const buffer = Buffer.from(compressedCode, 'base64');
|
|
200
|
+
return gunzipSync(buffer).toString('utf8');
|
|
201
|
+
}
|
|
202
|
+
const toolCode = decompressCode(tool.execute);
|
|
203
|
+
// Create a CommonJS context for execution
|
|
204
|
+
const require = createRequire(process.cwd() + '/package.json');
|
|
205
|
+
// Create a sandbox context
|
|
206
|
+
const sandbox = {
|
|
207
|
+
require,
|
|
208
|
+
console,
|
|
209
|
+
Buffer,
|
|
210
|
+
setTimeout,
|
|
211
|
+
setInterval,
|
|
212
|
+
clearTimeout,
|
|
213
|
+
clearInterval,
|
|
214
|
+
process,
|
|
215
|
+
global: globalThis,
|
|
216
|
+
__dirname: process.cwd(),
|
|
217
|
+
__filename: path.join(process.cwd(), 'index.ts'),
|
|
218
|
+
module: { exports: {} },
|
|
219
|
+
exports: {},
|
|
220
|
+
// Web APIs
|
|
221
|
+
fetch: globalThis.fetch,
|
|
222
|
+
URLSearchParams: globalThis.URLSearchParams,
|
|
223
|
+
URL: globalThis.URL,
|
|
224
|
+
Headers: globalThis.Headers,
|
|
225
|
+
Request: globalThis.Request,
|
|
226
|
+
Response: globalThis.Response,
|
|
227
|
+
// Additional globals
|
|
228
|
+
Object,
|
|
229
|
+
Array,
|
|
230
|
+
String,
|
|
231
|
+
Number,
|
|
232
|
+
Boolean,
|
|
233
|
+
Date,
|
|
234
|
+
Math,
|
|
235
|
+
JSON,
|
|
236
|
+
Error,
|
|
237
|
+
TypeError,
|
|
238
|
+
ReferenceError,
|
|
239
|
+
SyntaxError,
|
|
240
|
+
globalThis,
|
|
241
|
+
undefined: undefined,
|
|
242
|
+
null: null,
|
|
243
|
+
Infinity: Infinity,
|
|
244
|
+
NaN: NaN
|
|
245
|
+
};
|
|
246
|
+
// Create the CommonJS wrapper code
|
|
247
|
+
const commonJsWrapper = `
|
|
248
|
+
const executeFunction = ${toolCode};
|
|
249
|
+
|
|
250
|
+
// Export the function for testing
|
|
251
|
+
module.exports = async (input) => {
|
|
252
|
+
return await executeFunction(input);
|
|
253
|
+
};
|
|
254
|
+
`;
|
|
255
|
+
// Execute the code in the sandbox
|
|
256
|
+
const context = vm.createContext(sandbox);
|
|
257
|
+
vm.runInContext(commonJsWrapper, context);
|
|
258
|
+
// Get the exported function and execute it
|
|
259
|
+
const executeFunction = context.module.exports;
|
|
260
|
+
const result = await executeFunction(inputs);
|
|
261
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
262
|
+
res.end(JSON.stringify({
|
|
263
|
+
success: true,
|
|
264
|
+
result: result
|
|
265
|
+
}));
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
269
|
+
res.end(JSON.stringify({
|
|
270
|
+
success: false,
|
|
271
|
+
error: error.message || 'Tool execution failed'
|
|
272
|
+
}));
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
if (req.method === 'GET' && req.url === '/') {
|
|
278
|
+
// Read the React app HTML template
|
|
279
|
+
const htmlTemplate = fs.readFileSync(path.join(__dirname, '..', '..', 'src', 'web', 'index.html'), 'utf8');
|
|
280
|
+
// Replace placeholders with actual values
|
|
281
|
+
const html = htmlTemplate
|
|
282
|
+
.replace('{{API_KEY}}', apiKey)
|
|
283
|
+
.replace('{{AGENT_ID}}', agentId)
|
|
284
|
+
.replace('{{SKILL_ID}}', skillId)
|
|
285
|
+
.replace('{{SANDBOX_ID}}', sandboxId);
|
|
286
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
287
|
+
res.end(html);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
// Serve the React app bundle
|
|
291
|
+
if (req.method === 'GET' && req.url === '/app.js') {
|
|
292
|
+
try {
|
|
293
|
+
const appJs = fs.readFileSync(path.join(__dirname, '..', '..', 'dist', 'web', 'app.js'), 'utf8');
|
|
294
|
+
res.writeHead(200, { 'Content-Type': 'application/javascript' });
|
|
295
|
+
res.end(appJs);
|
|
296
|
+
}
|
|
297
|
+
catch (error) {
|
|
298
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
299
|
+
res.end('React app not found. Please run the build process.');
|
|
300
|
+
}
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
// Serve the CSS files
|
|
304
|
+
if (req.method === 'GET' && req.url === '/app.css') {
|
|
305
|
+
try {
|
|
306
|
+
const appCss = fs.readFileSync(path.join(__dirname, '..', '..', 'dist', 'web', 'app.css'), 'utf8');
|
|
307
|
+
res.writeHead(200, { 'Content-Type': 'text/css' });
|
|
308
|
+
res.end(appCss);
|
|
309
|
+
}
|
|
310
|
+
catch (error) {
|
|
311
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
312
|
+
res.end('CSS not found');
|
|
313
|
+
}
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
if (req.method === 'GET' && req.url === '/tools-page.css') {
|
|
317
|
+
try {
|
|
318
|
+
const toolsPageCss = fs.readFileSync(path.join(__dirname, '..', '..', 'dist', 'web', 'tools-page.css'), 'utf8');
|
|
319
|
+
res.writeHead(200, { 'Content-Type': 'text/css' });
|
|
320
|
+
res.end(toolsPageCss);
|
|
321
|
+
}
|
|
322
|
+
catch (error) {
|
|
323
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
324
|
+
res.end('Tools page CSS not found');
|
|
325
|
+
}
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
res.writeHead(404);
|
|
329
|
+
res.end('Not Found');
|
|
330
|
+
});
|
|
331
|
+
// Create WebSocket server for live reload
|
|
332
|
+
const wss = new WebSocketServer({ server });
|
|
333
|
+
wss.on('connection', (ws) => {
|
|
334
|
+
console.log('WebSocket client connected for live reload');
|
|
335
|
+
ws.on('close', () => {
|
|
336
|
+
console.log('WebSocket client disconnected');
|
|
337
|
+
});
|
|
338
|
+
ws.on('error', (error) => {
|
|
339
|
+
console.error('WebSocket error:', error);
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
server.listen(port, () => {
|
|
343
|
+
console.log('Chat interface available at: http://localhost:' + port);
|
|
344
|
+
console.log('Live reload WebSocket available at: ws://localhost:' + port);
|
|
345
|
+
});
|
|
346
|
+
return { server, wss };
|
|
347
|
+
}
|
|
348
|
+
// Function to update existing sandbox version
|
|
349
|
+
export async function updateDevVersion(apiKey, agentId, skillId, sandboxVersionId, versionData) {
|
|
350
|
+
try {
|
|
351
|
+
const url = 'http://localhost:3022/developer/skills/' + agentId + '/' + skillId + '/version/sandbox/' + sandboxVersionId;
|
|
352
|
+
console.log('Calling UPDATE endpoint: ' + url);
|
|
353
|
+
const response = await ApiService.Skill.updateDevSkill(apiKey, agentId, skillId, sandboxVersionId, versionData);
|
|
354
|
+
if (response.success) {
|
|
355
|
+
return {
|
|
356
|
+
success: true,
|
|
357
|
+
data: response.data
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
return {
|
|
362
|
+
success: false,
|
|
363
|
+
error: {
|
|
364
|
+
message: response.error?.message || 'Unknown error',
|
|
365
|
+
error: response.error?.message || 'Unknown error',
|
|
366
|
+
statusCode: response.error?.statusCode || 500
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
catch (error) {
|
|
372
|
+
console.error("Network error updating dev version:", error);
|
|
373
|
+
return {
|
|
374
|
+
success: false,
|
|
375
|
+
error: {
|
|
376
|
+
message: "Network error",
|
|
377
|
+
error: error instanceof Error ? error.message : "Failed to connect to server",
|
|
378
|
+
statusCode: 500
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
// Function to create new sandbox version
|
|
384
|
+
export async function pushDevVersion(apiKey, agentId, skillId, versionData) {
|
|
385
|
+
try {
|
|
386
|
+
const url = `http://localhost:3022/developer/skills/${agentId}/${skillId}/version/sandbox`;
|
|
387
|
+
console.log(`🔗 Calling CREATE endpoint: ${url}`);
|
|
388
|
+
const response = await ApiService.Skill.pushDevSkill(apiKey, agentId, skillId, versionData);
|
|
389
|
+
if (response.success) {
|
|
390
|
+
return {
|
|
391
|
+
success: true,
|
|
392
|
+
data: response.data
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
else {
|
|
396
|
+
return {
|
|
397
|
+
success: false,
|
|
398
|
+
error: {
|
|
399
|
+
message: response.error?.message || 'Unknown error',
|
|
400
|
+
error: response.error?.message || 'Unknown error',
|
|
401
|
+
statusCode: response.error?.statusCode || 500
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
catch (error) {
|
|
407
|
+
console.error("❌ Network error pushing dev version:", error);
|
|
408
|
+
return {
|
|
409
|
+
success: false,
|
|
410
|
+
error: {
|
|
411
|
+
message: "Network error",
|
|
412
|
+
error: error instanceof Error ? error.message : "Failed to connect to server",
|
|
413
|
+
statusCode: 500
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
function readConfigSkillName() {
|
|
419
|
+
const config = readSkillConfig();
|
|
420
|
+
return config?.skill?.name || null;
|
|
421
|
+
}
|
|
422
|
+
function readConfigVersion() {
|
|
423
|
+
const config = readSkillConfig();
|
|
424
|
+
return config?.skill?.version || null;
|
|
425
|
+
}
|
|
426
|
+
function readDeployJson() {
|
|
427
|
+
const deployPath = path.join(process.cwd(), '.lua', 'deploy.json');
|
|
428
|
+
if (!fs.existsSync(deployPath)) {
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
const deployContent = fs.readFileSync(deployPath, 'utf8');
|
|
432
|
+
return JSON.parse(deployContent);
|
|
433
|
+
}
|
|
434
|
+
// Function to push to sandbox (extracted for reuse)
|
|
435
|
+
async function pushToSandbox(apiKey, agentId, skillId, deployData, isInitial = false) {
|
|
436
|
+
const sandboxSkillId = await getSandboxSkillId();
|
|
437
|
+
// Read skill name from config and add to payload
|
|
438
|
+
const skillName = readConfigSkillName();
|
|
439
|
+
const payloadWithName = {
|
|
440
|
+
...deployData,
|
|
441
|
+
name: skillName || deployData.name || 'Unknown Skill'
|
|
442
|
+
};
|
|
443
|
+
if (sandboxSkillId) {
|
|
444
|
+
// Try to update existing sandbox version
|
|
445
|
+
if (!isInitial) {
|
|
446
|
+
writeProgress("🔄 Updating existing sandbox version...");
|
|
447
|
+
}
|
|
448
|
+
const updateResult = await updateDevVersion(apiKey, agentId, skillId, sandboxSkillId, payloadWithName);
|
|
449
|
+
if (updateResult.success && updateResult.data) {
|
|
450
|
+
if (!isInitial) {
|
|
451
|
+
writeSuccess(`✅ Version ${updateResult.data.version} updated in sandbox successfully`);
|
|
452
|
+
writeSuccess(`🔑 Sandbox Skill ID: ${updateResult.data.skillId}`);
|
|
453
|
+
}
|
|
454
|
+
return true;
|
|
455
|
+
}
|
|
456
|
+
else if (updateResult.error) {
|
|
457
|
+
if (!isInitial) {
|
|
458
|
+
writeProgress("⚠️ Failed to update existing sandbox, creating new one...");
|
|
459
|
+
console.error(`❌ Sandbox update failed: ${updateResult.error.message}`);
|
|
460
|
+
if (updateResult.error.error) {
|
|
461
|
+
console.error(` Details: ${updateResult.error.error}`);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
// Fall through to create new sandbox
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
// Create new sandbox version (either no existing ID or update failed)
|
|
468
|
+
if (!isInitial) {
|
|
469
|
+
writeProgress("🔄 Creating new sandbox version...");
|
|
470
|
+
}
|
|
471
|
+
const result = await pushDevVersion(apiKey, agentId, skillId, payloadWithName);
|
|
472
|
+
if (result.success && result.data) {
|
|
473
|
+
// Store the new sandbox skill ID
|
|
474
|
+
await setSandboxSkillId(result.data.skillId);
|
|
475
|
+
if (!isInitial) {
|
|
476
|
+
writeSuccess(`✅ Version ${result.data.version} pushed to sandbox successfully`);
|
|
477
|
+
writeSuccess(`🔑 Sandbox Skill ID: ${result.data.skillId}`);
|
|
478
|
+
}
|
|
479
|
+
return true;
|
|
480
|
+
}
|
|
481
|
+
else if (result.error) {
|
|
482
|
+
if (!isInitial) {
|
|
483
|
+
console.error(`❌ Sandbox creation failed: ${result.error.message}`);
|
|
484
|
+
if (result.error.error) {
|
|
485
|
+
console.error(` Details: ${result.error.error}`);
|
|
486
|
+
}
|
|
487
|
+
if (result.error.statusCode) {
|
|
488
|
+
console.error(` Status Code: ${result.error.statusCode}`);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
return false;
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
if (!isInitial) {
|
|
495
|
+
console.error("❌ Failed to push version to sandbox. Please try again.");
|
|
496
|
+
}
|
|
497
|
+
return false;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
export async function devCommand() {
|
|
501
|
+
return withErrorHandling(async () => {
|
|
502
|
+
// Check if we're in a skill directory
|
|
503
|
+
const config = readSkillConfig();
|
|
504
|
+
if (!config) {
|
|
505
|
+
console.error("❌ No lua.skill.yaml found. Please run this command from a skill directory.");
|
|
506
|
+
process.exit(1);
|
|
507
|
+
}
|
|
508
|
+
// Read version from config
|
|
509
|
+
const version = config.skill?.version;
|
|
510
|
+
if (!version) {
|
|
511
|
+
console.error("❌ No version found in skill configuration");
|
|
512
|
+
process.exit(1);
|
|
513
|
+
}
|
|
514
|
+
// Confirm with user
|
|
515
|
+
const { confirmed } = await inquirer.prompt([
|
|
516
|
+
{
|
|
517
|
+
type: "confirm",
|
|
518
|
+
name: "confirmed",
|
|
519
|
+
message: `Are you sure you want to push version ${version} to sandbox?`,
|
|
520
|
+
default: false
|
|
521
|
+
}
|
|
522
|
+
]);
|
|
523
|
+
// Clear the confirmation prompt lines
|
|
524
|
+
clearPromptLines(2);
|
|
525
|
+
if (!confirmed) {
|
|
526
|
+
console.log("❌ Dev push cancelled.");
|
|
527
|
+
process.exit(0);
|
|
528
|
+
}
|
|
529
|
+
// Load API key
|
|
530
|
+
const apiKey = await loadApiKey();
|
|
531
|
+
if (!apiKey) {
|
|
532
|
+
console.error("❌ No API key found. Please run 'lua auth configure' to set up your API key.");
|
|
533
|
+
process.exit(1);
|
|
534
|
+
}
|
|
535
|
+
// Validate API key
|
|
536
|
+
const userData = await checkApiKey(apiKey);
|
|
537
|
+
writeProgress("✅ Authenticated");
|
|
538
|
+
// Compile the skill first
|
|
539
|
+
writeProgress("🔄 Compiling skill...");
|
|
540
|
+
await compileCommand();
|
|
541
|
+
// Read deploy.json
|
|
542
|
+
const deployData = readDeployJson();
|
|
543
|
+
if (!deployData) {
|
|
544
|
+
console.error("❌ No deploy.json found. Compilation may have failed.");
|
|
545
|
+
process.exit(1);
|
|
546
|
+
}
|
|
547
|
+
// Verify version matches
|
|
548
|
+
if (deployData.version !== version) {
|
|
549
|
+
console.error(`❌ Version mismatch: config has ${version}, deploy.json has ${deployData.version}`);
|
|
550
|
+
process.exit(1);
|
|
551
|
+
}
|
|
552
|
+
// Extract agentId and skillId from config
|
|
553
|
+
const agentId = config.agent?.agentId;
|
|
554
|
+
const skillId = config.skill?.skillId;
|
|
555
|
+
if (!agentId || !skillId) {
|
|
556
|
+
console.error("❌ Missing agentId or skillId in skill configuration");
|
|
557
|
+
process.exit(1);
|
|
558
|
+
}
|
|
559
|
+
// Initial push to sandbox
|
|
560
|
+
writeProgress("🔄 Pushing to sandbox...");
|
|
561
|
+
const initialSuccess = await pushToSandbox(apiKey, agentId, skillId, deployData, true);
|
|
562
|
+
if (!initialSuccess) {
|
|
563
|
+
console.error("❌ Failed to push to sandbox. Cannot start development mode.");
|
|
564
|
+
process.exit(1);
|
|
565
|
+
}
|
|
566
|
+
// Get the sandbox skill ID for chat
|
|
567
|
+
const sandboxSkillId = await getSandboxSkillId();
|
|
568
|
+
if (!sandboxSkillId) {
|
|
569
|
+
console.error("❌ No sandbox skill ID found. Cannot start chat interface.");
|
|
570
|
+
process.exit(1);
|
|
571
|
+
}
|
|
572
|
+
// Start web server for chat interface
|
|
573
|
+
const chatPort = 3000;
|
|
574
|
+
const { server, wss } = createChatServer(apiKey, agentId, skillId, sandboxSkillId, chatPort);
|
|
575
|
+
// Open browser to chat interface
|
|
576
|
+
try {
|
|
577
|
+
await open(`http://localhost:${chatPort}`);
|
|
578
|
+
writeSuccess("🌐 Chat interface opened in your browser");
|
|
579
|
+
}
|
|
580
|
+
catch (error) {
|
|
581
|
+
writeSuccess(`🌐 Chat interface available at: http://localhost:${chatPort}`);
|
|
582
|
+
}
|
|
583
|
+
// Start file watching
|
|
584
|
+
writeSuccess("🔍 Watching for file changes... (Press Ctrl+C to stop)");
|
|
585
|
+
let isCompiling = false;
|
|
586
|
+
let pendingCompile = false;
|
|
587
|
+
const watcher = watch(process.cwd(), { recursive: true }, async (eventType, filename) => {
|
|
588
|
+
// Ignore changes in .lua directory and other build artifacts
|
|
589
|
+
if (filename && (filename.includes('.lua/') ||
|
|
590
|
+
filename.includes('node_modules/') ||
|
|
591
|
+
filename.includes('.git/') ||
|
|
592
|
+
filename.endsWith('.log') ||
|
|
593
|
+
filename.endsWith('.tmp'))) {
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
// Debounce rapid changes
|
|
597
|
+
if (isCompiling) {
|
|
598
|
+
pendingCompile = true;
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
isCompiling = true;
|
|
602
|
+
pendingCompile = false;
|
|
603
|
+
try {
|
|
604
|
+
writeProgress(`🔄 File changed: ${filename} - Compiling and pushing...`);
|
|
605
|
+
// Compile the skill
|
|
606
|
+
await compileCommand();
|
|
607
|
+
// Read updated deploy.json
|
|
608
|
+
const updatedDeployData = readDeployJson();
|
|
609
|
+
if (!updatedDeployData) {
|
|
610
|
+
writeProgress("❌ Compilation failed, skipping push");
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
// Verify version matches
|
|
614
|
+
if (updatedDeployData.version !== version) {
|
|
615
|
+
writeProgress("❌ Version mismatch, skipping push");
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
// Push to sandbox
|
|
619
|
+
const pushSuccess = await pushToSandbox(apiKey, agentId, skillId, updatedDeployData, false);
|
|
620
|
+
if (!pushSuccess) {
|
|
621
|
+
writeProgress("❌ Failed to push to sandbox, will retry on next change");
|
|
622
|
+
}
|
|
623
|
+
else {
|
|
624
|
+
// Broadcast reload message to all connected WebSocket clients
|
|
625
|
+
wss.clients.forEach((client) => {
|
|
626
|
+
if (client.readyState === 1) { // WebSocket.OPEN
|
|
627
|
+
client.send(JSON.stringify({ type: 'reload', message: 'Files changed, reloading...' }));
|
|
628
|
+
}
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
catch (error) {
|
|
633
|
+
writeProgress(`❌ Error during auto-compile: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
634
|
+
}
|
|
635
|
+
finally {
|
|
636
|
+
isCompiling = false;
|
|
637
|
+
// Handle any pending compile
|
|
638
|
+
if (pendingCompile) {
|
|
639
|
+
setTimeout(() => {
|
|
640
|
+
if (!isCompiling) {
|
|
641
|
+
watcher.emit('change', 'pending', 'pending');
|
|
642
|
+
}
|
|
643
|
+
}, 100);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
});
|
|
647
|
+
// Handle graceful shutdown
|
|
648
|
+
process.on('SIGINT', () => {
|
|
649
|
+
writeSuccess("\n🛑 Stopping file watcher...");
|
|
650
|
+
watcher.close();
|
|
651
|
+
process.exit(0);
|
|
652
|
+
});
|
|
653
|
+
// Keep the process alive
|
|
654
|
+
return new Promise(() => { });
|
|
655
|
+
}, "dev push");
|
|
656
|
+
}
|
package/dist/commands/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ export { configureCommand } from "./configure.js";
|
|
|
2
2
|
export { initCommand } from "./init.js";
|
|
3
3
|
export { destroyCommand } from "./destroy.js";
|
|
4
4
|
export { apiKeyCommand } from "./apiKey.js";
|
|
5
|
-
export {
|
|
6
|
-
export { deployCommand } from "./deploy.js";
|
|
5
|
+
export { compileCommand } from "./compile.js";
|
|
7
6
|
export { testCommand } from "./test.js";
|
|
7
|
+
export { pushCommand } from "./push.js";
|
|
8
|
+
export { deployCommand } from "./deploy.js";
|
|
9
|
+
export { devCommand } from "./dev.js";
|
package/dist/commands/index.js
CHANGED
|
@@ -2,6 +2,8 @@ export { configureCommand } from "./configure.js";
|
|
|
2
2
|
export { initCommand } from "./init.js";
|
|
3
3
|
export { destroyCommand } from "./destroy.js";
|
|
4
4
|
export { apiKeyCommand } from "./apiKey.js";
|
|
5
|
-
export {
|
|
6
|
-
export { deployCommand } from "./deploy.js";
|
|
5
|
+
export { compileCommand } from "./compile.js";
|
|
7
6
|
export { testCommand } from "./test.js";
|
|
7
|
+
export { pushCommand } from "./push.js";
|
|
8
|
+
export { deployCommand } from "./deploy.js";
|
|
9
|
+
export { devCommand } from "./dev.js";
|