lua-cli 2.3.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/api/products.api.service.js +1 -0
  2. package/dist/cli/command-definitions.d.ts +0 -8
  3. package/dist/cli/command-definitions.js +51 -18
  4. package/dist/commands/chat.d.ts +17 -0
  5. package/dist/commands/chat.js +236 -0
  6. package/dist/commands/chatClear.d.ts +15 -0
  7. package/dist/commands/chatClear.js +75 -0
  8. package/dist/commands/deploy.d.ts +7 -5
  9. package/dist/commands/deploy.js +34 -15
  10. package/dist/commands/env.d.ts +19 -0
  11. package/dist/commands/env.js +434 -0
  12. package/dist/commands/index.d.ts +6 -0
  13. package/dist/commands/index.js +6 -0
  14. package/dist/commands/init.js +6 -6
  15. package/dist/commands/persona.d.ts +15 -0
  16. package/dist/commands/persona.js +503 -0
  17. package/dist/commands/production.d.ts +15 -0
  18. package/dist/commands/production.js +532 -0
  19. package/dist/commands/push.d.ts +7 -5
  20. package/dist/commands/push.js +100 -21
  21. package/dist/commands/resources.d.ts +17 -0
  22. package/dist/commands/resources.js +361 -0
  23. package/dist/common/data.entry.instance.js +6 -2
  24. package/dist/common/http.client.js +7 -0
  25. package/dist/config/compile.constants.d.ts +4 -3
  26. package/dist/config/compile.constants.js +3 -7
  27. package/dist/config/constants.d.ts +6 -0
  28. package/dist/config/constants.js +11 -0
  29. package/dist/errors/auth.error.d.ts +15 -0
  30. package/dist/errors/auth.error.js +27 -0
  31. package/dist/errors/index.d.ts +4 -0
  32. package/dist/errors/index.js +4 -0
  33. package/dist/index.d.ts +0 -9
  34. package/dist/index.js +30 -11
  35. package/dist/interfaces/agent.d.ts +33 -49
  36. package/dist/services/auth.d.ts +1 -2
  37. package/dist/services/auth.js +3 -4
  38. package/dist/utils/bundling.js +61 -0
  39. package/dist/utils/cli.js +6 -0
  40. package/dist/utils/deploy-helpers.d.ts +22 -0
  41. package/dist/utils/deploy-helpers.js +61 -2
  42. package/dist/utils/deployment.js +33 -1
  43. package/dist/utils/dev-server.js +255 -0
  44. package/dist/utils/init-agent.js +3 -3
  45. package/dist/utils/prompt-handler.d.ts +12 -0
  46. package/dist/utils/prompt-handler.js +31 -0
  47. package/dist/utils/push-helpers.d.ts +47 -3
  48. package/dist/utils/push-helpers.js +167 -10
  49. package/dist/utils/sandbox.js +18 -5
  50. package/dist/web/app.css +1302 -465
  51. package/dist/web/app.js +53 -46
  52. package/package.json +1 -1
  53. package/template/package.json +1 -1
  54. package/template/src/tools/BasketTool.ts +4 -1
  55. package/template/src/tools/ProductsTool.ts +7 -7
  56. package/dist/web/tools-page.css +0 -381
@@ -0,0 +1,532 @@
1
+ /**
2
+ * Production Command
3
+ * Overview and management of production environment
4
+ */
5
+ import { loadApiKey, checkApiKey } from '../services/auth.js';
6
+ import { readSkillConfig } from '../utils/files.js';
7
+ import { withErrorHandling, writeProgress, writeSuccess } from '../utils/cli.js';
8
+ import { BASE_URLS } from '../config/constants.js';
9
+ import { safePrompt } from '../utils/prompt-handler.js';
10
+ import { validateConfig, validateAgentConfig, } from '../utils/dev-helpers.js';
11
+ /**
12
+ * Main production command - overview of production environment
13
+ *
14
+ * Features:
15
+ * - View current persona and versions
16
+ * - View deployed skills and versions
17
+ * - Manage environment variables
18
+ *
19
+ * @returns Promise that resolves when command completes
20
+ */
21
+ export async function productionCommand() {
22
+ return withErrorHandling(async () => {
23
+ // Step 1: Load configuration
24
+ const config = readSkillConfig();
25
+ validateConfig(config);
26
+ validateAgentConfig(config);
27
+ const agentId = config.agent.agentId;
28
+ // Step 2: Authenticate
29
+ const apiKey = await loadApiKey();
30
+ if (!apiKey) {
31
+ console.error("❌ No API key found. Please run 'lua auth configure' to set up your API key.");
32
+ process.exit(1);
33
+ }
34
+ await checkApiKey(apiKey);
35
+ writeProgress("✅ Authenticated");
36
+ const context = {
37
+ agentId,
38
+ apiKey,
39
+ };
40
+ // Step 3: Start management loop
41
+ await manageProduction(context, config);
42
+ }, "production");
43
+ }
44
+ /**
45
+ * Main production management loop
46
+ */
47
+ async function manageProduction(context, config) {
48
+ let continueManaging = true;
49
+ while (continueManaging) {
50
+ console.log("\n" + "=".repeat(60));
51
+ console.log("🌙 Production Environment");
52
+ console.log("=".repeat(60) + "\n");
53
+ const actionAnswer = await safePrompt([
54
+ {
55
+ type: 'list',
56
+ name: 'action',
57
+ message: 'What would you like to view?',
58
+ choices: [
59
+ { name: '🤖 Persona - View current persona and versions', value: 'persona' },
60
+ { name: '⚙️ Skills - View deployed skills and versions', value: 'skills' },
61
+ { name: '🔐 Environment Variables - Manage production env', value: 'env' },
62
+ { name: '❌ Exit', value: 'exit' }
63
+ ]
64
+ }
65
+ ]);
66
+ if (!actionAnswer)
67
+ return;
68
+ const { action } = actionAnswer;
69
+ switch (action) {
70
+ case 'persona':
71
+ await viewProductionPersona(context);
72
+ break;
73
+ case 'skills':
74
+ await viewProductionSkills(context, config);
75
+ break;
76
+ case 'env':
77
+ await manageProductionEnv(context);
78
+ break;
79
+ case 'exit':
80
+ continueManaging = false;
81
+ console.log("\n👋 Goodbye!\n");
82
+ break;
83
+ }
84
+ }
85
+ }
86
+ /**
87
+ * View production persona and versions
88
+ */
89
+ async function viewProductionPersona(context) {
90
+ writeProgress("🔄 Loading persona information...");
91
+ try {
92
+ const response = await fetch(`${BASE_URLS.API}/developer/agents/${context.agentId}/persona/versions`, {
93
+ method: 'GET',
94
+ headers: {
95
+ 'Authorization': `Bearer ${context.apiKey}`,
96
+ 'Content-Type': 'application/json'
97
+ }
98
+ });
99
+ if (!response.ok) {
100
+ const errorText = await response.text();
101
+ console.error(`\n❌ API Error: ${response.status} - ${errorText}\n`);
102
+ throw new Error(`HTTP error! status: ${response.status}`);
103
+ }
104
+ const data = await response.json();
105
+ let versions = [];
106
+ if (Array.isArray(data)) {
107
+ versions = data;
108
+ }
109
+ else if (data.data && Array.isArray(data.data)) {
110
+ versions = data.data;
111
+ }
112
+ else if (data.versions && Array.isArray(data.versions)) {
113
+ versions = data.versions;
114
+ }
115
+ if (versions.length === 0) {
116
+ console.log("\nℹ️ No persona versions found.");
117
+ console.log("💡 Create a persona version first using: lua persona\n");
118
+ await safePrompt([
119
+ {
120
+ type: 'input',
121
+ name: 'continue',
122
+ message: 'Press Enter to continue...'
123
+ }
124
+ ]);
125
+ return;
126
+ }
127
+ // Find current deployed version
128
+ const currentVersion = versions.find(v => v.isCurrent);
129
+ console.log("\n" + "=".repeat(60));
130
+ console.log("🤖 Current Production Persona");
131
+ console.log("=".repeat(60) + "\n");
132
+ if (currentVersion) {
133
+ const date = new Date(currentVersion.createdDate);
134
+ console.log(`Version: ${currentVersion.version} ⭐`);
135
+ console.log(`Deployed: ${date.toLocaleString()}`);
136
+ if (currentVersion.createdBy) {
137
+ console.log(`Created by: ${currentVersion.createdBy}`);
138
+ }
139
+ console.log("\n" + "-".repeat(60) + "\n");
140
+ console.log(currentVersion.persona);
141
+ console.log("\n" + "=".repeat(60) + "\n");
142
+ }
143
+ else {
144
+ console.log("⚠️ No persona currently deployed.");
145
+ console.log("💡 Deploy a version using: lua persona → Production → Deploy\n");
146
+ console.log("Available versions:\n");
147
+ versions.forEach((v, i) => {
148
+ const date = new Date(v.createdDate);
149
+ console.log(` ${i + 1}. Version ${v.version} - ${date.toLocaleDateString()}`);
150
+ });
151
+ console.log();
152
+ }
153
+ // Show available versions
154
+ console.log(`Total versions available: ${versions.length}\n`);
155
+ await safePrompt([
156
+ {
157
+ type: 'input',
158
+ name: 'continue',
159
+ message: 'Press Enter to continue...'
160
+ }
161
+ ]);
162
+ }
163
+ catch (error) {
164
+ console.error('❌ Error loading persona information:', error);
165
+ if (error instanceof Error) {
166
+ console.error(` ${error.message}\n`);
167
+ }
168
+ await safePrompt([
169
+ {
170
+ type: 'input',
171
+ name: 'continue',
172
+ message: 'Press Enter to continue...'
173
+ }
174
+ ]);
175
+ }
176
+ }
177
+ /**
178
+ * View production skills and their versions
179
+ */
180
+ async function viewProductionSkills(context, config) {
181
+ writeProgress("🔄 Loading skill information...");
182
+ try {
183
+ const skills = config.skills || [];
184
+ if (skills.length === 0) {
185
+ console.log("\nℹ️ No skills found in configuration.\n");
186
+ return;
187
+ }
188
+ console.log("\n" + "=".repeat(60));
189
+ console.log("⚙️ Production Skills");
190
+ console.log("=".repeat(60) + "\n");
191
+ // Fetch version info for each skill
192
+ for (const skill of skills) {
193
+ try {
194
+ const response = await fetch(`${BASE_URLS.API}/developer/skills/${context.agentId}/${skill.skillId}/versions`, {
195
+ method: 'GET',
196
+ headers: {
197
+ 'Authorization': `Bearer ${context.apiKey}`,
198
+ 'Content-Type': 'application/json'
199
+ }
200
+ });
201
+ if (response.ok) {
202
+ const data = await response.json();
203
+ const versions = data.data?.versions || data.versions || [];
204
+ const activeVersionId = data.data?.activeVersionId || data.activeVersionId;
205
+ // Find active version
206
+ const activeVersion = versions.find((v) => v.skillId === activeVersionId);
207
+ console.log(`📦 ${skill.name}`);
208
+ console.log(` Skill ID: ${skill.skillId}`);
209
+ if (activeVersion) {
210
+ console.log(` Deployed Version: ${activeVersion.version} ⭐`);
211
+ const date = new Date(activeVersion.createdAt);
212
+ console.log(` Deployed: ${date.toLocaleString()}`);
213
+ }
214
+ else {
215
+ console.log(` Deployed Version: Not deployed`);
216
+ }
217
+ console.log(` Total Versions: ${versions.length}`);
218
+ console.log();
219
+ }
220
+ else {
221
+ console.log(`📦 ${skill.name}`);
222
+ console.log(` Skill ID: ${skill.skillId}`);
223
+ console.log(` Status: Unable to fetch version info`);
224
+ console.log();
225
+ }
226
+ }
227
+ catch (error) {
228
+ console.log(`📦 ${skill.name}`);
229
+ console.log(` Skill ID: ${skill.skillId}`);
230
+ console.log(` Status: Error loading versions`);
231
+ console.log();
232
+ }
233
+ }
234
+ console.log("=".repeat(60) + "\n");
235
+ await safePrompt([
236
+ {
237
+ type: 'input',
238
+ name: 'continue',
239
+ message: 'Press Enter to continue...'
240
+ }
241
+ ]);
242
+ }
243
+ catch (error) {
244
+ console.error('❌ Error loading skill information:', error);
245
+ }
246
+ }
247
+ /**
248
+ * Manage production environment variables
249
+ */
250
+ async function manageProductionEnv(context) {
251
+ let continueManaging = true;
252
+ while (continueManaging) {
253
+ // Load current variables
254
+ const variables = await loadProductionEnvVariables(context);
255
+ console.log("\n" + "=".repeat(60));
256
+ console.log("🔐 Production Environment Variables");
257
+ console.log("=".repeat(60) + "\n");
258
+ if (variables.length === 0) {
259
+ console.log("ℹ️ No environment variables configured.\n");
260
+ }
261
+ else {
262
+ variables.forEach((v, index) => {
263
+ // Mask values partially for security
264
+ const maskedValue = v.value.length > 4
265
+ ? v.value.substring(0, 4) + '*'.repeat(Math.min(v.value.length - 4, 20))
266
+ : '*'.repeat(v.value.length);
267
+ console.log(`${index + 1}. ${v.key} = ${maskedValue}`);
268
+ });
269
+ console.log();
270
+ }
271
+ const actionAnswer = await safePrompt([
272
+ {
273
+ type: 'list',
274
+ name: 'action',
275
+ message: 'What would you like to do?',
276
+ choices: [
277
+ { name: '➕ Add new variable', value: 'add' },
278
+ { name: '✏️ Update existing variable', value: 'update' },
279
+ { name: '🗑️ Delete variable', value: 'delete' },
280
+ { name: '👁️ View variable value', value: 'view' },
281
+ { name: '🔄 Refresh list', value: 'refresh' },
282
+ { name: '⬅️ Back to main menu', value: 'back' }
283
+ ]
284
+ }
285
+ ]);
286
+ if (!actionAnswer)
287
+ return;
288
+ const { action } = actionAnswer;
289
+ switch (action) {
290
+ case 'add':
291
+ await addProductionVariable(context, variables);
292
+ break;
293
+ case 'update':
294
+ await updateProductionVariable(context, variables);
295
+ break;
296
+ case 'delete':
297
+ await deleteProductionVariable(context, variables);
298
+ break;
299
+ case 'view':
300
+ await viewProductionVariable(variables);
301
+ break;
302
+ case 'refresh':
303
+ // Just loop again
304
+ break;
305
+ case 'back':
306
+ continueManaging = false;
307
+ break;
308
+ }
309
+ }
310
+ }
311
+ /**
312
+ * Load production environment variables
313
+ */
314
+ async function loadProductionEnvVariables(context) {
315
+ try {
316
+ const response = await fetch(`${BASE_URLS.API}/developer/agents/${context.agentId}/env`, {
317
+ method: 'GET',
318
+ headers: {
319
+ 'accept': 'application/json',
320
+ 'Authorization': `Bearer ${context.apiKey}`
321
+ }
322
+ });
323
+ if (!response.ok) {
324
+ return [];
325
+ }
326
+ const data = await response.json();
327
+ if (data.data && typeof data.data === 'object') {
328
+ return Object.entries(data.data).map(([key, value]) => ({
329
+ key,
330
+ value: String(value)
331
+ }));
332
+ }
333
+ return [];
334
+ }
335
+ catch (error) {
336
+ console.error('⚠️ Error loading environment variables');
337
+ return [];
338
+ }
339
+ }
340
+ /**
341
+ * Save production environment variables
342
+ */
343
+ async function saveProductionEnvVariables(context, variables) {
344
+ try {
345
+ const envData = {};
346
+ variables.forEach(v => {
347
+ if (v.key.trim()) {
348
+ envData[v.key.trim()] = v.value;
349
+ }
350
+ });
351
+ const response = await fetch(`${BASE_URLS.API}/developer/agents/${context.agentId}/env`, {
352
+ method: 'POST',
353
+ headers: {
354
+ 'accept': 'application/json',
355
+ 'Authorization': `Bearer ${context.apiKey}`,
356
+ 'Content-Type': 'application/json'
357
+ },
358
+ body: JSON.stringify(envData)
359
+ });
360
+ if (!response.ok) {
361
+ throw new Error(`HTTP error! status: ${response.status}`);
362
+ }
363
+ return true;
364
+ }
365
+ catch (error) {
366
+ console.error('❌ Error saving environment variables:', error);
367
+ return false;
368
+ }
369
+ }
370
+ /**
371
+ * Add a new production environment variable
372
+ */
373
+ async function addProductionVariable(context, currentVariables) {
374
+ const answers = await safePrompt([
375
+ {
376
+ type: 'input',
377
+ name: 'key',
378
+ message: 'Variable name:',
379
+ validate: (input) => {
380
+ if (!input.trim())
381
+ return 'Variable name is required';
382
+ if (!/^[A-Z_][A-Z0-9_]*$/i.test(input.trim())) {
383
+ return 'Variable name must start with a letter or underscore and contain only letters, numbers, and underscores';
384
+ }
385
+ if (currentVariables.some(v => v.key === input.trim())) {
386
+ return 'Variable already exists. Use update to modify it.';
387
+ }
388
+ return true;
389
+ }
390
+ },
391
+ {
392
+ type: 'input',
393
+ name: 'value',
394
+ message: 'Variable value:',
395
+ }
396
+ ]);
397
+ if (!answers)
398
+ return;
399
+ const newVariable = {
400
+ key: answers.key.trim(),
401
+ value: answers.value
402
+ };
403
+ const updatedVariables = [...currentVariables, newVariable];
404
+ writeProgress("🔄 Saving...");
405
+ const success = await saveProductionEnvVariables(context, updatedVariables);
406
+ if (success) {
407
+ writeSuccess(`✅ Variable "${newVariable.key}" added successfully`);
408
+ }
409
+ else {
410
+ console.error("❌ Failed to save variable");
411
+ }
412
+ }
413
+ /**
414
+ * Update an existing production environment variable
415
+ */
416
+ async function updateProductionVariable(context, currentVariables) {
417
+ if (currentVariables.length === 0) {
418
+ console.log("\nℹ️ No variables to update.\n");
419
+ return;
420
+ }
421
+ const selectAnswer = await safePrompt([
422
+ {
423
+ type: 'list',
424
+ name: 'selectedKey',
425
+ message: 'Select variable to update:',
426
+ choices: currentVariables.map(v => ({
427
+ name: v.key,
428
+ value: v.key
429
+ }))
430
+ }
431
+ ]);
432
+ if (!selectAnswer)
433
+ return;
434
+ const currentVariable = currentVariables.find(v => v.key === selectAnswer.selectedKey);
435
+ const valueAnswer = await safePrompt([
436
+ {
437
+ type: 'input',
438
+ name: 'newValue',
439
+ message: `New value for ${selectAnswer.selectedKey}:`,
440
+ default: currentVariable.value
441
+ }
442
+ ]);
443
+ if (!valueAnswer)
444
+ return;
445
+ const updatedVariables = currentVariables.map(v => v.key === selectAnswer.selectedKey ? { ...v, value: valueAnswer.newValue } : v);
446
+ writeProgress("🔄 Saving...");
447
+ const success = await saveProductionEnvVariables(context, updatedVariables);
448
+ if (success) {
449
+ writeSuccess(`✅ Variable "${selectAnswer.selectedKey}" updated successfully`);
450
+ }
451
+ else {
452
+ console.error("❌ Failed to update variable");
453
+ }
454
+ }
455
+ /**
456
+ * Delete a production environment variable
457
+ */
458
+ async function deleteProductionVariable(context, currentVariables) {
459
+ if (currentVariables.length === 0) {
460
+ console.log("\nℹ️ No variables to delete.\n");
461
+ return;
462
+ }
463
+ const selectAnswer = await safePrompt([
464
+ {
465
+ type: 'list',
466
+ name: 'selectedKey',
467
+ message: 'Select variable to delete:',
468
+ choices: currentVariables.map(v => ({
469
+ name: v.key,
470
+ value: v.key
471
+ }))
472
+ }
473
+ ]);
474
+ if (!selectAnswer)
475
+ return;
476
+ const confirmAnswer = await safePrompt([
477
+ {
478
+ type: 'confirm',
479
+ name: 'confirm',
480
+ message: `Are you sure you want to delete "${selectAnswer.selectedKey}" from production?`,
481
+ default: false
482
+ }
483
+ ]);
484
+ if (!confirmAnswer || !confirmAnswer.confirm) {
485
+ console.log("\nℹ️ Deletion cancelled.\n");
486
+ return;
487
+ }
488
+ const updatedVariables = currentVariables.filter(v => v.key !== selectAnswer.selectedKey);
489
+ writeProgress("🔄 Saving...");
490
+ const success = await saveProductionEnvVariables(context, updatedVariables);
491
+ if (success) {
492
+ writeSuccess(`✅ Variable "${selectAnswer.selectedKey}" deleted successfully`);
493
+ }
494
+ else {
495
+ console.error("❌ Failed to delete variable");
496
+ }
497
+ }
498
+ /**
499
+ * View a variable's full value (unmasked)
500
+ */
501
+ async function viewProductionVariable(currentVariables) {
502
+ if (currentVariables.length === 0) {
503
+ console.log("\nℹ️ No variables to view.\n");
504
+ return;
505
+ }
506
+ const selectAnswer = await safePrompt([
507
+ {
508
+ type: 'list',
509
+ name: 'selectedKey',
510
+ message: 'Select variable to view:',
511
+ choices: currentVariables.map(v => ({
512
+ name: v.key,
513
+ value: v.key
514
+ }))
515
+ }
516
+ ]);
517
+ if (!selectAnswer)
518
+ return;
519
+ const variable = currentVariables.find(v => v.key === selectAnswer.selectedKey);
520
+ console.log("\n" + "=".repeat(60));
521
+ console.log(`Variable: ${variable.key}`);
522
+ console.log("=".repeat(60));
523
+ console.log(variable.value);
524
+ console.log("=".repeat(60) + "\n");
525
+ await safePrompt([
526
+ {
527
+ type: 'input',
528
+ name: 'continue',
529
+ message: 'Press Enter to continue...'
530
+ }
531
+ ]);
532
+ }
@@ -7,11 +7,13 @@
7
7
  *
8
8
  * This command performs the following steps:
9
9
  * 1. Validates configuration has required fields
10
- * 2. Prompts for user confirmation
11
- * 3. Authenticates the user
12
- * 4. Compiles the skill
13
- * 5. Validates deploy.json matches configuration
14
- * 6. Pushes version to server
10
+ * 2. Prompts user to select a skill (if multiple skills exist)
11
+ * 3. Prompts to confirm or update version
12
+ * 4. Authenticates the user
13
+ * 5. Compiles the skill (always, to ensure deploy.json is current)
14
+ * 6. Validates deploy.json matches configuration for selected skill
15
+ * 7. Extracts the specific skill's deploy data
16
+ * 8. Pushes version to server
15
17
  *
16
18
  * Use this command to:
17
19
  * - Upload a new version of your skill