lua-cli 1.3.2-alpha.3 → 2.0.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.
@@ -45,7 +45,7 @@ export async function deployCommand() {
45
45
  // Load API key
46
46
  const apiKey = await loadApiKey();
47
47
  if (!apiKey) {
48
- console.error("❌ No API key found. Please run 'lua configure' to set up your API key.");
48
+ console.error("❌ No API key found. Please run 'lua auth configure' to set up your API key.");
49
49
  process.exit(1);
50
50
  }
51
51
  // Validate API key
@@ -1,13 +1,12 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { fileURLToPath } from 'url';
4
- import inquirer from 'inquirer';
5
4
  import { compileCommand } from './compile.js';
6
5
  import { checkApiKey, loadApiKey } from '../services/auth.js';
7
6
  import { ApiService } from '../services/api.js';
8
- import { executeTool, createBroadcastConsole } from '../utils/sandbox.js';
7
+ import { executeTool, createBroadcastConsole, loadEnvironmentVariables } from '../utils/sandbox.js';
9
8
  import { readSkillConfig, updateSkillYamlPersona } from '../utils/files.js';
10
- import { withErrorHandling, clearPromptLines, writeProgress, writeSuccess } from '../utils/cli.js';
9
+ import { withErrorHandling, writeProgress, writeSuccess } from '../utils/cli.js';
11
10
  import keytar from 'keytar';
12
11
  import { watch } from 'fs';
13
12
  import { createServer } from 'http';
@@ -19,26 +18,64 @@ const __dirname = path.dirname(__filename);
19
18
  const SANDBOX_SERVICE = "lua-cli-sandbox";
20
19
  const SANDBOX_ACCOUNT = "sandbox-skill-id";
21
20
  // Function to get sandbox skill ID from secure storage
22
- async function getSandboxSkillId() {
21
+ async function getSandboxSkillId(skillName) {
23
22
  try {
24
- return await keytar.getPassword(SANDBOX_SERVICE, SANDBOX_ACCOUNT);
23
+ const account = skillName ? `${SANDBOX_ACCOUNT}_${skillName}` : SANDBOX_ACCOUNT;
24
+ return await keytar.getPassword(SANDBOX_SERVICE, account);
25
25
  }
26
26
  catch (error) {
27
27
  return null;
28
28
  }
29
29
  }
30
30
  // Function to store sandbox skill ID in secure storage
31
- async function setSandboxSkillId(skillId) {
31
+ async function setSandboxSkillId(sandboxId, skillName) {
32
32
  try {
33
- await keytar.setPassword(SANDBOX_SERVICE, SANDBOX_ACCOUNT, skillId);
33
+ const account = skillName ? `${SANDBOX_ACCOUNT}_${skillName}` : SANDBOX_ACCOUNT;
34
+ await keytar.setPassword(SANDBOX_SERVICE, account, sandboxId);
34
35
  }
35
36
  catch (error) {
36
37
  // Ignore storage errors
37
38
  }
38
39
  }
40
+ // Function to get all sandbox skill IDs for all skills
41
+ async function getAllSandboxSkillIds() {
42
+ const skillOverrides = [];
43
+ try {
44
+ // Read deploy.json to get all skills
45
+ const deployData = readDeployJson();
46
+ if (!deployData || !deployData.skills || !Array.isArray(deployData.skills)) {
47
+ return skillOverrides;
48
+ }
49
+ // For each skill, get its sandbox ID
50
+ for (const skill of deployData.skills) {
51
+ if (skill.skillId && skill.name) {
52
+ const sandboxId = await getSandboxSkillId(skill.name);
53
+ if (sandboxId) {
54
+ skillOverrides.push({
55
+ skillId: skill.skillId,
56
+ sandboxId: sandboxId
57
+ });
58
+ }
59
+ }
60
+ }
61
+ }
62
+ catch (error) {
63
+ console.error('Error getting sandbox skill IDs:', error);
64
+ }
65
+ return skillOverrides;
66
+ }
39
67
  // Function to send chat message to API
40
68
  export async function sendChatMessage(apiKey, agentId, skillId, sandboxId, message, persona) {
41
69
  try {
70
+ // Get all sandbox skill IDs for skill override
71
+ const allSkillOverrides = await getAllSandboxSkillIds();
72
+ // If no skills found, fallback to the provided skillId and sandboxId
73
+ const skillOverride = allSkillOverrides.length > 0 ? allSkillOverrides : [
74
+ {
75
+ skillId: skillId,
76
+ sandboxId: sandboxId
77
+ }
78
+ ];
42
79
  const chatRequest = {
43
80
  messages: [
44
81
  {
@@ -47,12 +84,7 @@ export async function sendChatMessage(apiKey, agentId, skillId, sandboxId, messa
47
84
  }
48
85
  ],
49
86
  navigate: true,
50
- skillOverride: [
51
- {
52
- skillId: skillId,
53
- sandboxId: sandboxId
54
- }
55
- ]
87
+ skillOverride: skillOverride
56
88
  };
57
89
  // Add persona override if provided
58
90
  if (persona) {
@@ -122,6 +154,120 @@ function createChatServer(apiKey, agentId, skillId, sandboxId, port = 3000) {
122
154
  }
123
155
  return;
124
156
  }
157
+ if (req.method === 'GET' && req.url === '/api/config') {
158
+ try {
159
+ const config = readSkillConfig();
160
+ res.writeHead(200, { 'Content-Type': 'application/json' });
161
+ res.end(JSON.stringify({
162
+ agent: {
163
+ agentId: config?.agent?.agentId || '',
164
+ orgId: config?.agent?.orgId || ''
165
+ }
166
+ }));
167
+ }
168
+ catch (error) {
169
+ console.error('Error reading config:', error);
170
+ res.writeHead(500, { 'Content-Type': 'application/json' });
171
+ res.end(JSON.stringify({ error: 'Failed to read configuration' }));
172
+ }
173
+ return;
174
+ }
175
+ if (req.method === 'GET' && req.url === '/api/skills') {
176
+ (async () => {
177
+ try {
178
+ const skillsData = [];
179
+ // First try to read from deploy.json in the .lua directory
180
+ const deployPath = path.join(process.cwd(), '.lua', 'deploy.json');
181
+ if (fs.existsSync(deployPath)) {
182
+ // Read from compiled deploy.json
183
+ const deployData = JSON.parse(fs.readFileSync(deployPath, 'utf8'));
184
+ if (deployData.skills && Array.isArray(deployData.skills)) {
185
+ // New format with skills array
186
+ for (const skill of deployData.skills) {
187
+ const sandboxId = await getSandboxSkillId(skill.name);
188
+ skillsData.push({
189
+ name: skill.name || 'Unnamed Skill',
190
+ description: skill.description || 'A Lua skill for AI automation',
191
+ context: skill.context || '',
192
+ version: skill.version || '1.0.0',
193
+ skillId: skill.skillId || '',
194
+ sandboxId: sandboxId || '',
195
+ tools: skill.tools ? skill.tools.map((tool) => tool.name) : []
196
+ });
197
+ }
198
+ }
199
+ else {
200
+ // Legacy format fallback
201
+ const toolNames = Object.keys(deployData.tools || {});
202
+ const sandboxId = await getSandboxSkillId(); // Legacy uses no skill name
203
+ skillsData.push({
204
+ name: deployData.name || 'Current Skill',
205
+ description: deployData.description || 'A Lua skill for AI automation',
206
+ context: deployData.context || '',
207
+ version: deployData.version || '1.0.0',
208
+ skillId: deployData.skillId || '',
209
+ sandboxId: sandboxId || '',
210
+ tools: toolNames
211
+ });
212
+ }
213
+ }
214
+ else {
215
+ // Fallback: Read from YAML and scan tools directory
216
+ const config = readSkillConfig();
217
+ const toolsDir = path.join(process.cwd(), 'src', 'tools');
218
+ let toolNames = [];
219
+ if (fs.existsSync(toolsDir)) {
220
+ const toolFiles = fs.readdirSync(toolsDir)
221
+ .filter((file) => file.endsWith('.ts'))
222
+ .map((file) => file.replace('.ts', ''));
223
+ toolNames = toolFiles;
224
+ }
225
+ if (config?.skills && Array.isArray(config.skills)) {
226
+ // New format: multiple skills
227
+ for (const skill of config.skills) {
228
+ const sandboxId = await getSandboxSkillId(skill.name);
229
+ skillsData.push({
230
+ name: skill.name || 'Unnamed Skill',
231
+ description: 'A Lua skill for AI automation (not compiled)',
232
+ context: 'This skill has not been compiled yet. Run "lua compile" to see full context information.',
233
+ version: skill.version || '1.0.0',
234
+ skillId: skill.skillId || '',
235
+ sandboxId: sandboxId || '',
236
+ tools: toolNames // All tools for now since not compiled
237
+ });
238
+ }
239
+ }
240
+ else if (config?.skill) {
241
+ // Legacy format: single skill
242
+ const sandboxId = await getSandboxSkillId(); // Legacy uses no skill name
243
+ skillsData.push({
244
+ name: config.skill.name || 'Current Skill',
245
+ description: 'A Lua skill for AI automation (not compiled)',
246
+ context: 'This skill has not been compiled yet. Run "lua compile" to see full context information.',
247
+ version: config.skill.version || '1.0.0',
248
+ skillId: config.skill.skillId || '',
249
+ sandboxId: sandboxId || '',
250
+ tools: toolNames
251
+ });
252
+ }
253
+ }
254
+ res.writeHead(200, { 'Content-Type': 'application/json' });
255
+ res.end(JSON.stringify({
256
+ success: true,
257
+ skills: skillsData
258
+ }));
259
+ }
260
+ catch (error) {
261
+ console.error('Error reading skills:', error);
262
+ res.writeHead(500, { 'Content-Type': 'application/json' });
263
+ res.end(JSON.stringify({
264
+ success: false,
265
+ error: 'Failed to read skills configuration'
266
+ }));
267
+ }
268
+ })();
269
+ return;
270
+ }
125
271
  if (req.method === 'POST' && req.url === '/persona') {
126
272
  let body = '';
127
273
  req.on('data', chunk => {
@@ -145,6 +291,50 @@ function createChatServer(apiKey, agentId, skillId, sandboxId, port = 3000) {
145
291
  });
146
292
  return;
147
293
  }
294
+ // Environment variables endpoints
295
+ if (req.method === 'GET' && req.url === '/env') {
296
+ try {
297
+ const envPath = path.join(process.cwd(), '.env');
298
+ let envContent = '';
299
+ if (fs.existsSync(envPath)) {
300
+ envContent = fs.readFileSync(envPath, 'utf8');
301
+ }
302
+ res.writeHead(200, { 'Content-Type': 'application/json' });
303
+ res.end(JSON.stringify({
304
+ success: true,
305
+ content: envContent
306
+ }));
307
+ }
308
+ catch (error) {
309
+ res.writeHead(500, { 'Content-Type': 'application/json' });
310
+ res.end(JSON.stringify({ success: false, error: 'Failed to read .env file' }));
311
+ }
312
+ return;
313
+ }
314
+ if (req.method === 'POST' && req.url === '/env') {
315
+ let body = '';
316
+ req.on('data', chunk => {
317
+ body += chunk.toString();
318
+ });
319
+ req.on('end', async () => {
320
+ try {
321
+ const { content } = JSON.parse(body);
322
+ const envPath = path.join(process.cwd(), '.env');
323
+ // Write the .env file
324
+ fs.writeFileSync(envPath, content || '', 'utf8');
325
+ res.writeHead(200, { 'Content-Type': 'application/json' });
326
+ res.end(JSON.stringify({
327
+ success: true,
328
+ message: 'Environment variables updated successfully'
329
+ }));
330
+ }
331
+ catch (error) {
332
+ res.writeHead(500, { 'Content-Type': 'application/json' });
333
+ res.end(JSON.stringify({ success: false, error: 'Failed to update .env file' }));
334
+ }
335
+ });
336
+ return;
337
+ }
148
338
  // Tools endpoints
149
339
  if (req.method === 'GET' && req.url === '/tools') {
150
340
  try {
@@ -152,10 +342,23 @@ function createChatServer(apiKey, agentId, skillId, sandboxId, port = 3000) {
152
342
  const deployPath = path.join(process.cwd(), '.lua', 'deploy.json');
153
343
  if (fs.existsSync(deployPath)) {
154
344
  const deployData = JSON.parse(fs.readFileSync(deployPath, 'utf8'));
345
+ // Handle both new skills array format and legacy tools format
346
+ let tools = deployData.tools || {};
347
+ if (deployData.skills && Array.isArray(deployData.skills)) {
348
+ // New format: merge all tools from all skills
349
+ tools = {};
350
+ deployData.skills.forEach((skill) => {
351
+ if (skill.tools && Array.isArray(skill.tools)) {
352
+ skill.tools.forEach((tool) => {
353
+ tools[tool.name] = tool;
354
+ });
355
+ }
356
+ });
357
+ }
155
358
  res.writeHead(200, { 'Content-Type': 'application/json' });
156
359
  res.end(JSON.stringify({
157
360
  success: true,
158
- tools: deployData.tools || []
361
+ tools: tools
159
362
  }));
160
363
  }
161
364
  else {
@@ -185,20 +388,33 @@ function createChatServer(apiKey, agentId, skillId, sandboxId, port = 3000) {
185
388
  return;
186
389
  }
187
390
  const deployData = JSON.parse(fs.readFileSync(deployPath, 'utf8'));
188
- const tool = deployData.tools.find((t) => t.name === toolName);
391
+ let tool = null;
392
+ if (deployData.skills && Array.isArray(deployData.skills)) {
393
+ // New format: search through skills array
394
+ for (const skill of deployData.skills) {
395
+ if (skill.tools && Array.isArray(skill.tools)) {
396
+ tool = skill.tools.find((t) => t.name === toolName);
397
+ if (tool)
398
+ break;
399
+ }
400
+ }
401
+ }
402
+ else {
403
+ // Legacy format: search in tools object
404
+ if (deployData.tools && deployData.tools[toolName]) {
405
+ tool = {
406
+ name: toolName,
407
+ ...deployData.tools[toolName]
408
+ };
409
+ }
410
+ }
189
411
  if (!tool) {
190
412
  res.writeHead(404, { 'Content-Type': 'application/json' });
191
413
  res.end(JSON.stringify({ success: false, error: 'Tool not found' }));
192
414
  return;
193
415
  }
194
- // Extract environment variables from YAML config
195
- const config = readSkillConfig();
196
- const envVars = {};
197
- if (config?.skill?.env) {
198
- for (const [key, value] of Object.entries(config.skill.env)) {
199
- envVars[key] = value;
200
- }
201
- }
416
+ // Load environment variables from all sources (.env + yaml)
417
+ const envVars = loadEnvironmentVariables();
202
418
  // Decompress and execute the tool
203
419
  const { gunzipSync } = await import('zlib');
204
420
  const { Buffer } = await import('buffer');
@@ -209,8 +425,21 @@ function createChatServer(apiKey, agentId, skillId, sandboxId, port = 3000) {
209
425
  return gunzipSync(buffer).toString('utf8');
210
426
  }
211
427
  const toolCode = decompressCode(tool.execute);
428
+ // Broadcast tool test start
429
+ wss.clients.forEach((client) => {
430
+ if (client.readyState === 1) {
431
+ client.send(JSON.stringify({
432
+ type: 'log',
433
+ subType: 'info',
434
+ message: `🧪 Testing tool: ${toolName}`,
435
+ timestamp: new Date().toISOString(),
436
+ id: Date.now().toString()
437
+ }));
438
+ }
439
+ });
212
440
  // Create custom console that captures output and sends via WebSocket
213
441
  const customConsole = createBroadcastConsole((logData) => {
442
+ console.log('Broadcasting tool log:', logData);
214
443
  wss.clients.forEach((client) => {
215
444
  if (client.readyState === 1) { // WebSocket.OPEN
216
445
  client.send(JSON.stringify(logData));
@@ -225,6 +454,18 @@ function createChatServer(apiKey, agentId, skillId, sandboxId, port = 3000) {
225
454
  agentId,
226
455
  customConsole
227
456
  });
457
+ // Broadcast tool test completion
458
+ wss.clients.forEach((client) => {
459
+ if (client.readyState === 1) {
460
+ client.send(JSON.stringify({
461
+ type: 'log',
462
+ subType: 'info',
463
+ message: `✅ Tool test completed: ${toolName}`,
464
+ timestamp: new Date().toISOString(),
465
+ id: (Date.now() + 1).toString()
466
+ }));
467
+ }
468
+ });
228
469
  res.writeHead(200, { 'Content-Type': 'application/json' });
229
470
  res.end(JSON.stringify({
230
471
  success: true,
@@ -337,8 +578,7 @@ function createChatServer(apiKey, agentId, skillId, sandboxId, port = 3000) {
337
578
  // Function to update existing sandbox version
338
579
  export async function updateDevVersion(apiKey, agentId, skillId, sandboxVersionId, versionData) {
339
580
  try {
340
- const url = 'http://localhost:3022/developer/skills/' + agentId + '/' + skillId + '/version/sandbox/' + sandboxVersionId;
341
- console.log('Calling UPDATE endpoint: ' + url);
581
+ // console.log('Calling UPDATE endpoint for sandbox version');
342
582
  const response = await ApiService.Skill.updateDevSkill(apiKey, agentId, skillId, sandboxVersionId, versionData);
343
583
  if (response.success) {
344
584
  return {
@@ -372,8 +612,7 @@ export async function updateDevVersion(apiKey, agentId, skillId, sandboxVersionI
372
612
  // Function to create new sandbox version
373
613
  export async function pushDevVersion(apiKey, agentId, skillId, versionData) {
374
614
  try {
375
- const url = `http://localhost:3022/developer/skills/${agentId}/${skillId}/version/sandbox`;
376
- console.log(`🔗 Calling CREATE endpoint: ${url}`);
615
+ // console.log(`🔗 Calling CREATE endpoint for sandbox version`);
377
616
  const response = await ApiService.Skill.pushDevSkill(apiKey, agentId, skillId, versionData);
378
617
  if (response.success) {
379
618
  return {
@@ -416,15 +655,42 @@ function readDeployJson() {
416
655
  const deployContent = fs.readFileSync(deployPath, 'utf8');
417
656
  return JSON.parse(deployContent);
418
657
  }
419
- // Function to push to sandbox (extracted for reuse)
420
- async function pushToSandbox(apiKey, agentId, skillId, deployData, isInitial = false) {
421
- const sandboxSkillId = await getSandboxSkillId();
422
- // Read skill name and environment variables from config
423
- const config = readSkillConfig();
424
- const skillName = config?.skill?.name || deployData.name || 'Unknown Skill';
425
- const envVars = config?.skill?.env || {};
658
+ // Function to push multiple skills to sandbox
659
+ async function pushSkillsToSandbox(apiKey, agentId, deployData, isInitial = false) {
660
+ const sandboxIds = {};
661
+ if (!deployData.skills || !Array.isArray(deployData.skills)) {
662
+ throw new Error("No skills found in deploy data");
663
+ }
664
+ for (const skill of deployData.skills) {
665
+ if (!skill.skillId) {
666
+ console.warn(`⚠️ Skipping skill ${skill.name} - no skillId found`);
667
+ continue;
668
+ }
669
+ try {
670
+ const success = await pushSingleSkillToSandbox(apiKey, agentId, skill.skillId, skill, isInitial);
671
+ if (success) {
672
+ // Get the sandbox ID for this skill (stored in .lua directory)
673
+ const sandboxId = await getSandboxSkillId(skill.name);
674
+ if (sandboxId) {
675
+ sandboxIds[skill.name] = sandboxId;
676
+ }
677
+ }
678
+ }
679
+ catch (error) {
680
+ console.error(`❌ Failed to push skill ${skill.name}:`, error);
681
+ }
682
+ }
683
+ return sandboxIds;
684
+ }
685
+ // Function to push a single skill to sandbox (extracted for reuse)
686
+ async function pushSingleSkillToSandbox(apiKey, agentId, skillId, skillData, isInitial = false) {
687
+ const skillName = skillData.name || 'Unknown Skill';
688
+ const sandboxSkillId = await getSandboxSkillId(skillName);
689
+ // Load environment variables
690
+ const envVars = loadEnvironmentVariables();
691
+ // console.log(skillData);
426
692
  const payloadWithNameAndEnv = {
427
- ...deployData,
693
+ ...skillData,
428
694
  name: skillName,
429
695
  env: envVars
430
696
  };
@@ -436,8 +702,8 @@ async function pushToSandbox(apiKey, agentId, skillId, deployData, isInitial = f
436
702
  const updateResult = await updateDevVersion(apiKey, agentId, skillId, sandboxSkillId, payloadWithNameAndEnv);
437
703
  if (updateResult.success && updateResult.data) {
438
704
  if (!isInitial) {
439
- writeSuccess(`✅ Version ${updateResult.data.version} updated in sandbox successfully`);
440
- writeSuccess(`🔑 Sandbox Skill ID: ${updateResult.data.skillId}`);
705
+ // writeSuccess(`✅ Version ${updateResult.data.version} updated in sandbox successfully`);
706
+ // writeSuccess(`🔑 Sandbox Skill ID: ${updateResult.data.skillId}`);
441
707
  }
442
708
  return true;
443
709
  }
@@ -459,7 +725,7 @@ async function pushToSandbox(apiKey, agentId, skillId, deployData, isInitial = f
459
725
  const result = await pushDevVersion(apiKey, agentId, skillId, payloadWithNameAndEnv);
460
726
  if (result.success && result.data) {
461
727
  // Store the new sandbox skill ID
462
- await setSandboxSkillId(result.data.skillId);
728
+ await setSandboxSkillId(result.data.skillId, skillName);
463
729
  if (!isInitial) {
464
730
  writeSuccess(`✅ Version ${result.data.version} pushed to sandbox successfully`);
465
731
  writeSuccess(`🔑 Sandbox Skill ID: ${result.data.skillId}`);
@@ -493,26 +759,13 @@ export async function devCommand() {
493
759
  console.error("❌ No lua.skill.yaml found. Please run this command from a skill directory.");
494
760
  process.exit(1);
495
761
  }
496
- // Read version from config
497
- const version = config.skill?.version;
498
- if (!version) {
499
- console.error("❌ No version found in skill configuration");
500
- process.exit(1);
762
+ // Read version from config (handle both old and new formats)
763
+ let version = config.skill?.version; // Legacy format
764
+ if (!version && config.skills && config.skills.length > 0) {
765
+ version = config.skills[0].version; // New format - use first skill's version
501
766
  }
502
- // Confirm with user
503
- const { confirmed } = await inquirer.prompt([
504
- {
505
- type: "confirm",
506
- name: "confirmed",
507
- message: `Are you sure you want to push version ${version} to sandbox?`,
508
- default: false
509
- }
510
- ]);
511
- // Clear the confirmation prompt lines
512
- clearPromptLines(2);
513
- if (!confirmed) {
514
- console.log("❌ Dev push cancelled.");
515
- process.exit(0);
767
+ if (!version) {
768
+ version = "1.0.0"; // Default version
516
769
  }
517
770
  // Load API key
518
771
  const apiKey = await loadApiKey();
@@ -526,33 +779,51 @@ export async function devCommand() {
526
779
  // Compile the skill first
527
780
  writeProgress("🔄 Compiling skill...");
528
781
  await compileCommand();
782
+ // Re-read config after compilation to get updated skillIds
783
+ const updatedConfig = readSkillConfig();
784
+ if (!updatedConfig) {
785
+ console.error("❌ Failed to read updated skill configuration after compilation.");
786
+ process.exit(1);
787
+ }
529
788
  // Read deploy.json
530
789
  const deployData = readDeployJson();
531
790
  if (!deployData) {
532
791
  console.error("❌ No deploy.json found. Compilation may have failed.");
533
792
  process.exit(1);
534
793
  }
535
- // Verify version matches
536
- if (deployData.version !== version) {
537
- console.error(`❌ Version mismatch: config has ${version}, deploy.json has ${deployData.version}`);
794
+ // Verify deploy.json has skills
795
+ if (!deployData.skills || !Array.isArray(deployData.skills) || deployData.skills.length === 0) {
796
+ console.error("❌ No skills found in deploy.json. Compilation may have failed.");
538
797
  process.exit(1);
539
798
  }
540
- // Extract agentId and skillId from config
541
- const agentId = config.agent?.agentId;
542
- const skillId = config.skill?.skillId;
543
- if (!agentId || !skillId) {
544
- console.error("❌ Missing agentId or skillId in skill configuration");
799
+ // Extract agentId from updated config
800
+ const agentId = updatedConfig.agent?.agentId;
801
+ if (!agentId) {
802
+ console.error("❌ Missing agentId in skill configuration");
803
+ process.exit(1);
804
+ }
805
+ // For multi-skill projects, we'll use the first skill's ID for sandbox
806
+ let skillId = updatedConfig.skill?.skillId; // Legacy format
807
+ if (!skillId && updatedConfig.skills && updatedConfig.skills.length > 0) {
808
+ skillId = updatedConfig.skills[0].skillId; // New format - use first skill
809
+ }
810
+ if (!skillId) {
811
+ console.error("❌ No skillId found after compilation. Skill creation may have failed.");
812
+ console.error("Available skills:", updatedConfig.skills);
545
813
  process.exit(1);
546
814
  }
547
815
  // Initial push to sandbox
548
- writeProgress("🔄 Pushing to sandbox...");
549
- const initialSuccess = await pushToSandbox(apiKey, agentId, skillId, deployData, true);
550
- if (!initialSuccess) {
551
- console.error("❌ Failed to push to sandbox. Cannot start development mode.");
816
+ writeProgress("🔄 Pushing skills to sandbox...");
817
+ const sandboxIds = await pushSkillsToSandbox(apiKey, agentId, deployData, true);
818
+ // console.log(sandboxIds);
819
+ if (Object.keys(sandboxIds).length === 0) {
820
+ console.error("❌ Failed to push any skills to sandbox. Cannot start development mode.");
552
821
  process.exit(1);
553
822
  }
554
- // Get the sandbox skill ID for chat
555
- const sandboxSkillId = await getSandboxSkillId();
823
+ writeSuccess(`✅ Pushed ${Object.keys(sandboxIds).length} skills to sandbox`);
824
+ // Use the first skill's sandbox ID for the web interface
825
+ const firstSkillName = Object.keys(sandboxIds)[0];
826
+ const sandboxSkillId = sandboxIds[firstSkillName];
556
827
  if (!sandboxSkillId) {
557
828
  console.error("❌ No sandbox skill ID found. Cannot start chat interface.");
558
829
  process.exit(1);
@@ -620,20 +891,20 @@ export async function devCommand() {
620
891
  writeProgress("❌ Compilation failed, skipping push");
621
892
  return;
622
893
  }
623
- // Verify version matches
624
- if (updatedDeployData.version !== version) {
625
- writeProgress("❌ Version mismatch, skipping push");
894
+ // Verify updated deploy.json has skills
895
+ if (!updatedDeployData.skills || !Array.isArray(updatedDeployData.skills) || updatedDeployData.skills.length === 0) {
896
+ writeProgress("❌ No skills found in updated deploy.json, skipping push");
626
897
  return;
627
898
  }
628
- // Push to sandbox
629
- const pushSuccess = await pushToSandbox(apiKey, agentId, skillId, updatedDeployData, false);
630
- if (!pushSuccess) {
631
- writeProgress("❌ Failed to push to sandbox, will retry on next change");
632
- broadcastLog("❌ Failed to push to sandbox, will retry on next change", 'error');
899
+ // Push all skills to sandbox
900
+ const updatedSandboxIds = await pushSkillsToSandbox(apiKey, agentId, updatedDeployData, false);
901
+ if (Object.keys(updatedSandboxIds).length === 0) {
902
+ writeProgress("❌ Failed to push any skills to sandbox, will retry on next change");
903
+ broadcastLog("❌ Failed to push skills to sandbox, will retry on next change", 'error');
633
904
  }
634
905
  else {
635
- writeProgress("✅ Successfully pushed to sandbox");
636
- broadcastLog("✅ Successfully pushed to sandbox", 'info');
906
+ // writeProgress(`✅ Successfully pushed ${Object.keys(updatedSandboxIds).length} skills to sandbox`);
907
+ broadcastLog(`✅ Successfully pushed ${Object.keys(updatedSandboxIds).length} skills to sandbox`, 'info');
637
908
  }
638
909
  }
639
910
  catch (error) {