lua-cli 2.3.2 → 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.
@@ -0,0 +1,434 @@
1
+ /**
2
+ * Environment Variables Command
3
+ * Manages environment variables for sandbox and production environments
4
+ */
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import inquirer from 'inquirer';
8
+ import { loadApiKey, checkApiKey } from '../services/auth.js';
9
+ import { readSkillConfig } from '../utils/files.js';
10
+ import { withErrorHandling, writeProgress, writeSuccess } from '../utils/cli.js';
11
+ import { BASE_URLS } from '../config/constants.js';
12
+ import { validateConfig, validateAgentConfig, } from '../utils/dev-helpers.js';
13
+ /**
14
+ * Main env command - manages environment variables
15
+ *
16
+ * Features:
17
+ * - Environment selection (sandbox or production)
18
+ * - List all environment variables
19
+ * - Add new variables
20
+ * - Update existing variables
21
+ * - Delete variables
22
+ * - Sandbox: manages .env file
23
+ * - Production: uses API endpoints
24
+ *
25
+ * @returns Promise that resolves when command completes
26
+ */
27
+ export async function envCommand() {
28
+ return withErrorHandling(async () => {
29
+ // Step 1: Load configuration
30
+ const config = readSkillConfig();
31
+ validateConfig(config);
32
+ validateAgentConfig(config);
33
+ const agentId = config.agent.agentId;
34
+ // Step 2: Select environment
35
+ const { environment } = await inquirer.prompt([
36
+ {
37
+ type: 'list',
38
+ name: 'environment',
39
+ message: 'Select environment:',
40
+ choices: [
41
+ { name: 'šŸ”§ Sandbox (.env file)', value: 'sandbox' },
42
+ { name: 'šŸš€ Production (API)', value: 'production' }
43
+ ]
44
+ }
45
+ ]);
46
+ let context = {
47
+ environment,
48
+ agentId,
49
+ apiKey: '',
50
+ };
51
+ // Step 3: Authenticate (needed for production)
52
+ if (environment === 'production') {
53
+ const apiKey = await loadApiKey();
54
+ if (!apiKey) {
55
+ console.error("āŒ No API key found. Please run 'lua auth configure' to set up your API key.");
56
+ process.exit(1);
57
+ }
58
+ await checkApiKey(apiKey);
59
+ context.apiKey = apiKey;
60
+ writeProgress("āœ… Authenticated");
61
+ }
62
+ // Step 4: Start management loop
63
+ await manageEnvironmentVariables(context);
64
+ }, "env");
65
+ }
66
+ /**
67
+ * Main management loop for environment variables
68
+ */
69
+ async function manageEnvironmentVariables(context) {
70
+ let continueManaging = true;
71
+ while (continueManaging) {
72
+ // Load current variables
73
+ const variables = await loadEnvironmentVariables(context);
74
+ // Show current state
75
+ console.log("\n" + "=".repeat(60));
76
+ console.log(`šŸŒ™ Environment Variables (${context.environment === 'sandbox' ? 'Sandbox' : 'Production'})`);
77
+ console.log("=".repeat(60) + "\n");
78
+ if (variables.length === 0) {
79
+ console.log("ā„¹ļø No environment variables configured.\n");
80
+ }
81
+ else {
82
+ variables.forEach((v, index) => {
83
+ // Mask values partially for security
84
+ const maskedValue = v.value.length > 4
85
+ ? v.value.substring(0, 4) + '*'.repeat(Math.min(v.value.length - 4, 20))
86
+ : '*'.repeat(v.value.length);
87
+ console.log(`${index + 1}. ${v.key} = ${maskedValue}`);
88
+ });
89
+ console.log();
90
+ }
91
+ // Show menu
92
+ const { action } = await inquirer.prompt([
93
+ {
94
+ type: 'list',
95
+ name: 'action',
96
+ message: 'What would you like to do?',
97
+ choices: [
98
+ { name: 'āž• Add new variable', value: 'add' },
99
+ { name: 'āœļø Update existing variable', value: 'update' },
100
+ { name: 'šŸ—‘ļø Delete variable', value: 'delete' },
101
+ { name: 'šŸ‘ļø View variable value', value: 'view' },
102
+ { name: 'šŸ”„ Refresh list', value: 'refresh' },
103
+ { name: 'āŒ Exit', value: 'exit' }
104
+ ]
105
+ }
106
+ ]);
107
+ switch (action) {
108
+ case 'add':
109
+ await addVariable(context, variables);
110
+ break;
111
+ case 'update':
112
+ await updateVariable(context, variables);
113
+ break;
114
+ case 'delete':
115
+ await deleteVariable(context, variables);
116
+ break;
117
+ case 'view':
118
+ await viewVariable(variables);
119
+ break;
120
+ case 'refresh':
121
+ // Just loop again
122
+ break;
123
+ case 'exit':
124
+ continueManaging = false;
125
+ console.log("\nšŸ‘‹ Goodbye!\n");
126
+ break;
127
+ }
128
+ }
129
+ }
130
+ /**
131
+ * Load environment variables based on context
132
+ */
133
+ async function loadEnvironmentVariables(context) {
134
+ if (context.environment === 'sandbox') {
135
+ return loadSandboxEnvVariables();
136
+ }
137
+ else {
138
+ return loadProductionEnvVariables(context);
139
+ }
140
+ }
141
+ /**
142
+ * Load sandbox environment variables from .env file
143
+ */
144
+ function loadSandboxEnvVariables() {
145
+ const envFilePath = path.join(process.cwd(), '.env');
146
+ if (!fs.existsSync(envFilePath)) {
147
+ return [];
148
+ }
149
+ try {
150
+ const content = fs.readFileSync(envFilePath, 'utf8');
151
+ return parseEnvContent(content);
152
+ }
153
+ catch (error) {
154
+ console.error('āŒ Error reading .env file:', error);
155
+ return [];
156
+ }
157
+ }
158
+ /**
159
+ * Load production environment variables from API
160
+ */
161
+ async function loadProductionEnvVariables(context) {
162
+ try {
163
+ const response = await fetch(`${BASE_URLS.API}/developer/agents/${context.agentId}/env`, {
164
+ method: 'GET',
165
+ headers: {
166
+ 'accept': 'application/json',
167
+ 'Authorization': `Bearer ${context.apiKey}`
168
+ }
169
+ });
170
+ if (!response.ok) {
171
+ throw new Error(`HTTP error! status: ${response.status}`);
172
+ }
173
+ const data = await response.json();
174
+ if (data.data && typeof data.data === 'object') {
175
+ return Object.entries(data.data).map(([key, value]) => ({
176
+ key,
177
+ value: String(value)
178
+ }));
179
+ }
180
+ return [];
181
+ }
182
+ catch (error) {
183
+ console.error('āŒ Error loading production env variables:', error);
184
+ return [];
185
+ }
186
+ }
187
+ /**
188
+ * Parse .env file content into variables
189
+ */
190
+ function parseEnvContent(content) {
191
+ if (!content.trim())
192
+ return [];
193
+ return content
194
+ .split('\n')
195
+ .map(line => line.trim())
196
+ .filter(line => line && !line.startsWith('#'))
197
+ .map(line => {
198
+ const [key, ...valueParts] = line.split('=');
199
+ return {
200
+ key: key?.trim() || '',
201
+ value: valueParts.join('=') || ''
202
+ };
203
+ })
204
+ .filter(item => item.key);
205
+ }
206
+ /**
207
+ * Convert variables array to .env file content
208
+ */
209
+ function variablesToEnvContent(variables) {
210
+ return variables
211
+ .map(v => `${v.key}=${v.value}`)
212
+ .join('\n');
213
+ }
214
+ /**
215
+ * Save sandbox environment variables to .env file
216
+ */
217
+ function saveSandboxEnvVariables(variables) {
218
+ const envFilePath = path.join(process.cwd(), '.env');
219
+ try {
220
+ const content = variablesToEnvContent(variables);
221
+ fs.writeFileSync(envFilePath, content, 'utf8');
222
+ return true;
223
+ }
224
+ catch (error) {
225
+ console.error('āŒ Error saving .env file:', error);
226
+ return false;
227
+ }
228
+ }
229
+ /**
230
+ * Save production environment variables via API
231
+ */
232
+ async function saveProductionEnvVariables(context, variables) {
233
+ try {
234
+ const envData = {};
235
+ variables.forEach(v => {
236
+ if (v.key.trim()) {
237
+ envData[v.key.trim()] = v.value;
238
+ }
239
+ });
240
+ const response = await fetch(`${BASE_URLS.API}/developer/agents/${context.agentId}/env`, {
241
+ method: 'POST',
242
+ headers: {
243
+ 'accept': 'application/json',
244
+ 'Authorization': `Bearer ${context.apiKey}`,
245
+ 'Content-Type': 'application/json'
246
+ },
247
+ body: JSON.stringify(envData)
248
+ });
249
+ if (!response.ok) {
250
+ throw new Error(`HTTP error! status: ${response.status}`);
251
+ }
252
+ return true;
253
+ }
254
+ catch (error) {
255
+ console.error('āŒ Error saving production env variables:', error);
256
+ return false;
257
+ }
258
+ }
259
+ /**
260
+ * Add a new environment variable
261
+ */
262
+ async function addVariable(context, currentVariables) {
263
+ const answers = await inquirer.prompt([
264
+ {
265
+ type: 'input',
266
+ name: 'key',
267
+ message: 'Variable name:',
268
+ validate: (input) => {
269
+ if (!input.trim())
270
+ return 'Variable name is required';
271
+ if (!/^[A-Z_][A-Z0-9_]*$/i.test(input.trim())) {
272
+ return 'Variable name must start with a letter or underscore and contain only letters, numbers, and underscores';
273
+ }
274
+ if (currentVariables.some(v => v.key === input.trim())) {
275
+ return 'Variable already exists. Use update to modify it.';
276
+ }
277
+ return true;
278
+ }
279
+ },
280
+ {
281
+ type: 'input',
282
+ name: 'value',
283
+ message: 'Variable value:',
284
+ validate: (input) => {
285
+ if (input === '')
286
+ return true; // Allow empty values
287
+ return true;
288
+ }
289
+ }
290
+ ]);
291
+ const newVariable = {
292
+ key: answers.key.trim(),
293
+ value: answers.value
294
+ };
295
+ const updatedVariables = [...currentVariables, newVariable];
296
+ writeProgress("šŸ”„ Saving...");
297
+ let success = false;
298
+ if (context.environment === 'sandbox') {
299
+ success = saveSandboxEnvVariables(updatedVariables);
300
+ }
301
+ else {
302
+ success = await saveProductionEnvVariables(context, updatedVariables);
303
+ }
304
+ if (success) {
305
+ writeSuccess(`āœ… Variable "${newVariable.key}" added successfully`);
306
+ }
307
+ else {
308
+ console.error("āŒ Failed to save variable");
309
+ }
310
+ }
311
+ /**
312
+ * Update an existing environment variable
313
+ */
314
+ async function updateVariable(context, currentVariables) {
315
+ if (currentVariables.length === 0) {
316
+ console.log("\nā„¹ļø No variables to update.\n");
317
+ return;
318
+ }
319
+ const { selectedKey } = await inquirer.prompt([
320
+ {
321
+ type: 'list',
322
+ name: 'selectedKey',
323
+ message: 'Select variable to update:',
324
+ choices: currentVariables.map(v => ({
325
+ name: v.key,
326
+ value: v.key
327
+ }))
328
+ }
329
+ ]);
330
+ const currentVariable = currentVariables.find(v => v.key === selectedKey);
331
+ const { newValue } = await inquirer.prompt([
332
+ {
333
+ type: 'input',
334
+ name: 'newValue',
335
+ message: `New value for ${selectedKey}:`,
336
+ default: currentVariable.value
337
+ }
338
+ ]);
339
+ const updatedVariables = currentVariables.map(v => v.key === selectedKey ? { ...v, value: newValue } : v);
340
+ writeProgress("šŸ”„ Saving...");
341
+ let success = false;
342
+ if (context.environment === 'sandbox') {
343
+ success = saveSandboxEnvVariables(updatedVariables);
344
+ }
345
+ else {
346
+ success = await saveProductionEnvVariables(context, updatedVariables);
347
+ }
348
+ if (success) {
349
+ writeSuccess(`āœ… Variable "${selectedKey}" updated successfully`);
350
+ }
351
+ else {
352
+ console.error("āŒ Failed to update variable");
353
+ }
354
+ }
355
+ /**
356
+ * Delete an environment variable
357
+ */
358
+ async function deleteVariable(context, currentVariables) {
359
+ if (currentVariables.length === 0) {
360
+ console.log("\nā„¹ļø No variables to delete.\n");
361
+ return;
362
+ }
363
+ const { selectedKey } = await inquirer.prompt([
364
+ {
365
+ type: 'list',
366
+ name: 'selectedKey',
367
+ message: 'Select variable to delete:',
368
+ choices: currentVariables.map(v => ({
369
+ name: v.key,
370
+ value: v.key
371
+ }))
372
+ }
373
+ ]);
374
+ const { confirm } = await inquirer.prompt([
375
+ {
376
+ type: 'confirm',
377
+ name: 'confirm',
378
+ message: `Are you sure you want to delete "${selectedKey}"?`,
379
+ default: false
380
+ }
381
+ ]);
382
+ if (!confirm) {
383
+ console.log("\nā„¹ļø Deletion cancelled.\n");
384
+ return;
385
+ }
386
+ const updatedVariables = currentVariables.filter(v => v.key !== selectedKey);
387
+ writeProgress("šŸ”„ Saving...");
388
+ let success = false;
389
+ if (context.environment === 'sandbox') {
390
+ success = saveSandboxEnvVariables(updatedVariables);
391
+ }
392
+ else {
393
+ success = await saveProductionEnvVariables(context, updatedVariables);
394
+ }
395
+ if (success) {
396
+ writeSuccess(`āœ… Variable "${selectedKey}" deleted successfully`);
397
+ }
398
+ else {
399
+ console.error("āŒ Failed to delete variable");
400
+ }
401
+ }
402
+ /**
403
+ * View a variable's full value (unmasked)
404
+ */
405
+ async function viewVariable(currentVariables) {
406
+ if (currentVariables.length === 0) {
407
+ console.log("\nā„¹ļø No variables to view.\n");
408
+ return;
409
+ }
410
+ const { selectedKey } = await inquirer.prompt([
411
+ {
412
+ type: 'list',
413
+ name: 'selectedKey',
414
+ message: 'Select variable to view:',
415
+ choices: currentVariables.map(v => ({
416
+ name: v.key,
417
+ value: v.key
418
+ }))
419
+ }
420
+ ]);
421
+ const variable = currentVariables.find(v => v.key === selectedKey);
422
+ console.log("\n" + "=".repeat(60));
423
+ console.log(`Variable: ${variable.key}`);
424
+ console.log("=".repeat(60));
425
+ console.log(variable.value);
426
+ console.log("=".repeat(60) + "\n");
427
+ await inquirer.prompt([
428
+ {
429
+ type: 'input',
430
+ name: 'continue',
431
+ message: 'Press Enter to continue...'
432
+ }
433
+ ]);
434
+ }
@@ -7,3 +7,9 @@ export { testCommand } from "./test.js";
7
7
  export { pushCommand } from "./push.js";
8
8
  export { deployCommand } from "./deploy.js";
9
9
  export { devCommand } from "./dev.js";
10
+ export { chatCommand } from "./chat.js";
11
+ export { chatClearCommand } from "./chatClear.js";
12
+ export { envCommand } from "./env.js";
13
+ export { personaCommand } from "./persona.js";
14
+ export { productionCommand } from "./production.js";
15
+ export { resourcesCommand } from "./resources.js";
@@ -7,3 +7,9 @@ export { testCommand } from "./test.js";
7
7
  export { pushCommand } from "./push.js";
8
8
  export { deployCommand } from "./deploy.js";
9
9
  export { devCommand } from "./dev.js";
10
+ export { chatCommand } from "./chat.js";
11
+ export { chatClearCommand } from "./chatClear.js";
12
+ export { envCommand } from "./env.js";
13
+ export { personaCommand } from "./persona.js";
14
+ export { productionCommand } from "./production.js";
15
+ export { resourcesCommand } from "./resources.js";
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Persona Command
3
+ * Manages agent persona for sandbox and production environments
4
+ */
5
+ /**
6
+ * Main persona command - manages agent persona
7
+ *
8
+ * Features:
9
+ * - Environment selection (sandbox or production)
10
+ * - Sandbox: view, edit, save persona + create versions
11
+ * - Production: list versions, view details, deploy versions
12
+ *
13
+ * @returns Promise that resolves when command completes
14
+ */
15
+ export declare function personaCommand(): Promise<void>;