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.
- package/dist/api/products.api.service.js +1 -0
- package/dist/cli/command-definitions.d.ts +0 -8
- package/dist/cli/command-definitions.js +51 -18
- package/dist/commands/chat.d.ts +17 -0
- package/dist/commands/chat.js +236 -0
- package/dist/commands/chatClear.d.ts +15 -0
- package/dist/commands/chatClear.js +75 -0
- package/dist/commands/env.d.ts +19 -0
- package/dist/commands/env.js +434 -0
- package/dist/commands/index.d.ts +6 -0
- package/dist/commands/index.js +6 -0
- package/dist/commands/persona.d.ts +15 -0
- package/dist/commands/persona.js +503 -0
- package/dist/commands/production.d.ts +15 -0
- package/dist/commands/production.js +532 -0
- package/dist/commands/push.js +56 -0
- package/dist/commands/resources.d.ts +17 -0
- package/dist/commands/resources.js +361 -0
- package/dist/common/data.entry.instance.js +6 -2
- package/dist/config/compile.constants.d.ts +4 -3
- package/dist/config/compile.constants.js +3 -7
- package/dist/config/constants.d.ts +6 -0
- package/dist/config/constants.js +11 -0
- package/dist/index.d.ts +0 -9
- package/dist/index.js +30 -11
- package/dist/utils/bundling.js +61 -0
- package/dist/utils/prompt-handler.d.ts +12 -0
- package/dist/utils/prompt-handler.js +31 -0
- package/dist/utils/push-helpers.js +36 -31
- package/dist/utils/sandbox.js +2 -2
- package/dist/web/app.js +1 -1
- package/package.json +1 -1
- package/template/src/tools/BasketTool.ts +4 -1
- package/template/src/tools/ProductsTool.ts +7 -7
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resources Command
|
|
3
|
+
* Manages agent resources (knowledge base documents)
|
|
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 resources command - manages agent resources
|
|
13
|
+
*
|
|
14
|
+
* Features:
|
|
15
|
+
* - List all resources
|
|
16
|
+
* - Create new resources
|
|
17
|
+
* - Update existing resources
|
|
18
|
+
* - Delete resources
|
|
19
|
+
* - View resource content
|
|
20
|
+
*
|
|
21
|
+
* @returns Promise that resolves when command completes
|
|
22
|
+
*/
|
|
23
|
+
export async function resourcesCommand() {
|
|
24
|
+
return withErrorHandling(async () => {
|
|
25
|
+
// Step 1: Load configuration
|
|
26
|
+
const config = readSkillConfig();
|
|
27
|
+
validateConfig(config);
|
|
28
|
+
validateAgentConfig(config);
|
|
29
|
+
const agentId = config.agent.agentId;
|
|
30
|
+
// Step 2: Authenticate
|
|
31
|
+
const apiKey = await loadApiKey();
|
|
32
|
+
if (!apiKey) {
|
|
33
|
+
console.error("❌ No API key found. Please run 'lua auth configure' to set up your API key.");
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
await checkApiKey(apiKey);
|
|
37
|
+
writeProgress("✅ Authenticated");
|
|
38
|
+
const context = {
|
|
39
|
+
agentId,
|
|
40
|
+
apiKey,
|
|
41
|
+
};
|
|
42
|
+
// Step 3: Start management loop
|
|
43
|
+
await manageResources(context);
|
|
44
|
+
}, "resources");
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Main resources management loop
|
|
48
|
+
*/
|
|
49
|
+
async function manageResources(context) {
|
|
50
|
+
let continueManaging = true;
|
|
51
|
+
while (continueManaging) {
|
|
52
|
+
// Load current resources
|
|
53
|
+
const resources = await loadResources(context);
|
|
54
|
+
console.log("\n" + "=".repeat(60));
|
|
55
|
+
console.log("📚 Agent Resources");
|
|
56
|
+
console.log("=".repeat(60) + "\n");
|
|
57
|
+
if (resources.length === 0) {
|
|
58
|
+
console.log("ℹ️ No resources configured.\n");
|
|
59
|
+
console.log("💡 Resources are knowledge base documents your agent can reference.\n");
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
resources.forEach((resource, index) => {
|
|
63
|
+
const preview = resource.content.length > 60
|
|
64
|
+
? resource.content.substring(0, 60) + '...'
|
|
65
|
+
: resource.content;
|
|
66
|
+
console.log(`${index + 1}. ${resource.name}`);
|
|
67
|
+
console.log(` Preview: ${preview}`);
|
|
68
|
+
console.log();
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
const actionAnswer = await safePrompt([
|
|
72
|
+
{
|
|
73
|
+
type: 'list',
|
|
74
|
+
name: 'action',
|
|
75
|
+
message: 'What would you like to do?',
|
|
76
|
+
choices: [
|
|
77
|
+
{ name: '➕ Create new resource', value: 'create' },
|
|
78
|
+
{ name: '✏️ Update existing resource', value: 'update' },
|
|
79
|
+
{ name: '🗑️ Delete resource', value: 'delete' },
|
|
80
|
+
{ name: '👁️ View resource content', value: 'view' },
|
|
81
|
+
{ name: '🔄 Refresh list', value: 'refresh' },
|
|
82
|
+
{ name: '❌ Exit', value: 'exit' }
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
]);
|
|
86
|
+
if (!actionAnswer)
|
|
87
|
+
return;
|
|
88
|
+
const { action } = actionAnswer;
|
|
89
|
+
switch (action) {
|
|
90
|
+
case 'create':
|
|
91
|
+
await createResource(context);
|
|
92
|
+
break;
|
|
93
|
+
case 'update':
|
|
94
|
+
await updateResource(context, resources);
|
|
95
|
+
break;
|
|
96
|
+
case 'delete':
|
|
97
|
+
await deleteResource(context, resources);
|
|
98
|
+
break;
|
|
99
|
+
case 'view':
|
|
100
|
+
await viewResource(resources);
|
|
101
|
+
break;
|
|
102
|
+
case 'refresh':
|
|
103
|
+
// Just loop again
|
|
104
|
+
break;
|
|
105
|
+
case 'exit':
|
|
106
|
+
continueManaging = false;
|
|
107
|
+
console.log("\n👋 Goodbye!\n");
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Load resources from API
|
|
114
|
+
*/
|
|
115
|
+
async function loadResources(context) {
|
|
116
|
+
try {
|
|
117
|
+
const response = await fetch(`${BASE_URLS.API}/admin/agents/${context.agentId}/resources`, {
|
|
118
|
+
method: 'GET',
|
|
119
|
+
headers: {
|
|
120
|
+
'accept': 'application/json',
|
|
121
|
+
'Authorization': `Bearer ${context.apiKey}`
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
if (!response.ok) {
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
const data = await response.json();
|
|
128
|
+
if (Array.isArray(data)) {
|
|
129
|
+
return data;
|
|
130
|
+
}
|
|
131
|
+
else if (data.data && Array.isArray(data.data)) {
|
|
132
|
+
return data.data;
|
|
133
|
+
}
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
console.error('⚠️ Error loading resources');
|
|
138
|
+
return [];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Create a new resource
|
|
143
|
+
*/
|
|
144
|
+
async function createResource(context) {
|
|
145
|
+
const answers = await safePrompt([
|
|
146
|
+
{
|
|
147
|
+
type: 'input',
|
|
148
|
+
name: 'name',
|
|
149
|
+
message: 'Resource name:',
|
|
150
|
+
validate: (input) => {
|
|
151
|
+
if (!input.trim())
|
|
152
|
+
return 'Resource name is required';
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
type: 'editor',
|
|
158
|
+
name: 'content',
|
|
159
|
+
message: 'Resource content (will open in your default editor):',
|
|
160
|
+
validate: (input) => {
|
|
161
|
+
if (!input.trim())
|
|
162
|
+
return 'Resource content cannot be empty';
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
]);
|
|
167
|
+
if (!answers)
|
|
168
|
+
return;
|
|
169
|
+
writeProgress("🔄 Creating resource...");
|
|
170
|
+
try {
|
|
171
|
+
const response = await fetch(`${BASE_URLS.API}/admin/agents/${context.agentId}/resources`, {
|
|
172
|
+
method: 'POST',
|
|
173
|
+
headers: {
|
|
174
|
+
'accept': 'application/json',
|
|
175
|
+
'Authorization': `Bearer ${context.apiKey}`,
|
|
176
|
+
'Content-Type': 'application/json'
|
|
177
|
+
},
|
|
178
|
+
body: JSON.stringify({
|
|
179
|
+
name: answers.name.trim(),
|
|
180
|
+
content: answers.content.trim()
|
|
181
|
+
})
|
|
182
|
+
});
|
|
183
|
+
if (!response.ok) {
|
|
184
|
+
const errorText = await response.text();
|
|
185
|
+
throw new Error(`HTTP error! status: ${response.status} - ${errorText}`);
|
|
186
|
+
}
|
|
187
|
+
writeSuccess(`✅ Resource "${answers.name}" created successfully`);
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
console.error('❌ Error creating resource:', error);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Update an existing resource
|
|
195
|
+
*/
|
|
196
|
+
async function updateResource(context, resources) {
|
|
197
|
+
if (resources.length === 0) {
|
|
198
|
+
console.log("\nℹ️ No resources to update.\n");
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const selectAnswer = await safePrompt([
|
|
202
|
+
{
|
|
203
|
+
type: 'list',
|
|
204
|
+
name: 'selectedId',
|
|
205
|
+
message: 'Select resource to update:',
|
|
206
|
+
choices: resources.map(r => ({
|
|
207
|
+
name: r.name,
|
|
208
|
+
value: r.id
|
|
209
|
+
}))
|
|
210
|
+
}
|
|
211
|
+
]);
|
|
212
|
+
if (!selectAnswer)
|
|
213
|
+
return;
|
|
214
|
+
const selectedResource = resources.find(r => r.id === selectAnswer.selectedId);
|
|
215
|
+
const updateAnswers = await safePrompt([
|
|
216
|
+
{
|
|
217
|
+
type: 'input',
|
|
218
|
+
name: 'name',
|
|
219
|
+
message: 'Resource name:',
|
|
220
|
+
default: selectedResource.name,
|
|
221
|
+
validate: (input) => {
|
|
222
|
+
if (!input.trim())
|
|
223
|
+
return 'Resource name is required';
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
type: 'editor',
|
|
229
|
+
name: 'content',
|
|
230
|
+
message: 'Resource content (will open in your default editor):',
|
|
231
|
+
default: selectedResource.content,
|
|
232
|
+
validate: (input) => {
|
|
233
|
+
if (!input.trim())
|
|
234
|
+
return 'Resource content cannot be empty';
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
]);
|
|
239
|
+
if (!updateAnswers)
|
|
240
|
+
return;
|
|
241
|
+
writeProgress("🔄 Updating resource...");
|
|
242
|
+
try {
|
|
243
|
+
const response = await fetch(`${BASE_URLS.API}/admin/agents/${context.agentId}/resources/${selectAnswer.selectedId}`, {
|
|
244
|
+
method: 'PUT',
|
|
245
|
+
headers: {
|
|
246
|
+
'accept': 'application/json',
|
|
247
|
+
'Authorization': `Bearer ${context.apiKey}`,
|
|
248
|
+
'Content-Type': 'application/json'
|
|
249
|
+
},
|
|
250
|
+
body: JSON.stringify({
|
|
251
|
+
name: updateAnswers.name.trim(),
|
|
252
|
+
content: updateAnswers.content.trim()
|
|
253
|
+
})
|
|
254
|
+
});
|
|
255
|
+
if (!response.ok) {
|
|
256
|
+
const errorText = await response.text();
|
|
257
|
+
throw new Error(`HTTP error! status: ${response.status} - ${errorText}`);
|
|
258
|
+
}
|
|
259
|
+
writeSuccess(`✅ Resource "${updateAnswers.name}" updated successfully`);
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
console.error('❌ Error updating resource:', error);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Delete a resource
|
|
267
|
+
*/
|
|
268
|
+
async function deleteResource(context, resources) {
|
|
269
|
+
if (resources.length === 0) {
|
|
270
|
+
console.log("\nℹ️ No resources to delete.\n");
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
const selectAnswer = await safePrompt([
|
|
274
|
+
{
|
|
275
|
+
type: 'list',
|
|
276
|
+
name: 'selectedId',
|
|
277
|
+
message: 'Select resource to delete:',
|
|
278
|
+
choices: resources.map(r => ({
|
|
279
|
+
name: r.name,
|
|
280
|
+
value: r.id
|
|
281
|
+
}))
|
|
282
|
+
}
|
|
283
|
+
]);
|
|
284
|
+
if (!selectAnswer)
|
|
285
|
+
return;
|
|
286
|
+
const selectedResource = resources.find(r => r.id === selectAnswer.selectedId);
|
|
287
|
+
const confirmAnswer = await safePrompt([
|
|
288
|
+
{
|
|
289
|
+
type: 'confirm',
|
|
290
|
+
name: 'confirm',
|
|
291
|
+
message: `Are you sure you want to delete "${selectedResource.name}"?`,
|
|
292
|
+
default: false
|
|
293
|
+
}
|
|
294
|
+
]);
|
|
295
|
+
if (!confirmAnswer || !confirmAnswer.confirm) {
|
|
296
|
+
console.log("\nℹ️ Deletion cancelled.\n");
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
writeProgress("🔄 Deleting resource...");
|
|
300
|
+
try {
|
|
301
|
+
const response = await fetch(`${BASE_URLS.API}/admin/agents/${context.agentId}/resources/${selectAnswer.selectedId}`, {
|
|
302
|
+
method: 'DELETE',
|
|
303
|
+
headers: {
|
|
304
|
+
'accept': '*/*',
|
|
305
|
+
'Authorization': `Bearer ${context.apiKey}`
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
if (!response.ok) {
|
|
309
|
+
const errorText = await response.text();
|
|
310
|
+
throw new Error(`HTTP error! status: ${response.status} - ${errorText}`);
|
|
311
|
+
}
|
|
312
|
+
writeSuccess(`✅ Resource "${selectedResource.name}" deleted successfully`);
|
|
313
|
+
}
|
|
314
|
+
catch (error) {
|
|
315
|
+
console.error('❌ Error deleting resource:', error);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* View a resource's full content
|
|
320
|
+
*/
|
|
321
|
+
async function viewResource(resources) {
|
|
322
|
+
if (resources.length === 0) {
|
|
323
|
+
console.log("\nℹ️ No resources to view.\n");
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
const selectAnswer = await safePrompt([
|
|
327
|
+
{
|
|
328
|
+
type: 'list',
|
|
329
|
+
name: 'selectedId',
|
|
330
|
+
message: 'Select resource to view:',
|
|
331
|
+
choices: resources.map(r => ({
|
|
332
|
+
name: r.name,
|
|
333
|
+
value: r.id
|
|
334
|
+
}))
|
|
335
|
+
}
|
|
336
|
+
]);
|
|
337
|
+
if (!selectAnswer)
|
|
338
|
+
return;
|
|
339
|
+
const resource = resources.find(r => r.id === selectAnswer.selectedId);
|
|
340
|
+
console.log("\n" + "=".repeat(60));
|
|
341
|
+
console.log(`📄 Resource: ${resource.name}`);
|
|
342
|
+
console.log("=".repeat(60));
|
|
343
|
+
if (resource.createdAt) {
|
|
344
|
+
const date = new Date(resource.createdAt);
|
|
345
|
+
console.log(`Created: ${date.toLocaleString()}`);
|
|
346
|
+
}
|
|
347
|
+
if (resource.updatedAt) {
|
|
348
|
+
const date = new Date(resource.updatedAt);
|
|
349
|
+
console.log(`Updated: ${date.toLocaleString()}`);
|
|
350
|
+
}
|
|
351
|
+
console.log("=".repeat(60) + "\n");
|
|
352
|
+
console.log(resource.content);
|
|
353
|
+
console.log("\n" + "=".repeat(60) + "\n");
|
|
354
|
+
await safePrompt([
|
|
355
|
+
{
|
|
356
|
+
type: 'input',
|
|
357
|
+
name: 'continue',
|
|
358
|
+
message: 'Press Enter to continue...'
|
|
359
|
+
}
|
|
360
|
+
]);
|
|
361
|
+
}
|
|
@@ -29,7 +29,9 @@ export default class DataEntryInstance {
|
|
|
29
29
|
toJSON() {
|
|
30
30
|
return {
|
|
31
31
|
...this.data,
|
|
32
|
-
score: this.score
|
|
32
|
+
score: this.score,
|
|
33
|
+
id: this.id,
|
|
34
|
+
collectionName: this.collectionName
|
|
33
35
|
};
|
|
34
36
|
}
|
|
35
37
|
/**
|
|
@@ -39,7 +41,9 @@ export default class DataEntryInstance {
|
|
|
39
41
|
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
40
42
|
return {
|
|
41
43
|
...this.data,
|
|
42
|
-
score: this.score
|
|
44
|
+
score: this.score,
|
|
45
|
+
id: this.id,
|
|
46
|
+
collectionName: this.collectionName
|
|
43
47
|
};
|
|
44
48
|
}
|
|
45
49
|
/**
|
|
@@ -33,10 +33,11 @@ export declare const SKILL_DEFAULTS: {
|
|
|
33
33
|
};
|
|
34
34
|
/**
|
|
35
35
|
* External packages that should not be bundled with tools
|
|
36
|
-
* These are
|
|
37
|
-
*
|
|
36
|
+
* These are native modules, or modules that should be provided by the runtime environment
|
|
37
|
+
* Note: lua-cli is NOT in this list because it's handled by the sandboxGlobalsPlugin
|
|
38
|
+
* which transforms imports to use sandbox globals instead
|
|
38
39
|
*/
|
|
39
|
-
export declare const EXTERNAL_PACKAGES: readonly ["
|
|
40
|
+
export declare const EXTERNAL_PACKAGES: readonly ["zod", "keytar", "esbuild", "commander", "inquirer", "node-fetch", "ws", "socket.io-client", "ts-morph"];
|
|
40
41
|
/**
|
|
41
42
|
* Common esbuild configuration for tool bundling
|
|
42
43
|
*/
|
|
@@ -32,15 +32,11 @@ export const SKILL_DEFAULTS = {
|
|
|
32
32
|
};
|
|
33
33
|
/**
|
|
34
34
|
* External packages that should not be bundled with tools
|
|
35
|
-
* These are
|
|
36
|
-
*
|
|
35
|
+
* These are native modules, or modules that should be provided by the runtime environment
|
|
36
|
+
* Note: lua-cli is NOT in this list because it's handled by the sandboxGlobalsPlugin
|
|
37
|
+
* which transforms imports to use sandbox globals instead
|
|
37
38
|
*/
|
|
38
39
|
export const EXTERNAL_PACKAGES = [
|
|
39
|
-
'lua-cli/skill',
|
|
40
|
-
'lua-cli',
|
|
41
|
-
'lua-cli/user-data-api',
|
|
42
|
-
'lua-cli/product-api',
|
|
43
|
-
'lua-cli/custom-data-api',
|
|
44
40
|
'zod',
|
|
45
41
|
'keytar',
|
|
46
42
|
'esbuild',
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base URLs for the API, Auth, and Chat - Production
|
|
3
|
+
*/
|
|
1
4
|
export declare const BASE_URLS: {
|
|
2
5
|
readonly API: "https://api.heylua.ai";
|
|
3
6
|
readonly AUTH: "https://auth.heylua.ai";
|
|
4
7
|
readonly CHAT: "https://api.heylua.ai";
|
|
5
8
|
};
|
|
9
|
+
/**
|
|
10
|
+
* Base URLs for the API, Auth, and Chat - Development
|
|
11
|
+
*/
|
package/dist/config/constants.js
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base URLs for the API, Auth, and Chat - Production
|
|
3
|
+
*/
|
|
1
4
|
export const BASE_URLS = {
|
|
2
5
|
API: 'https://api.heylua.ai',
|
|
3
6
|
AUTH: 'https://auth.heylua.ai',
|
|
4
7
|
CHAT: 'https://api.heylua.ai',
|
|
5
8
|
};
|
|
9
|
+
/**
|
|
10
|
+
* Base URLs for the API, Auth, and Chat - Development
|
|
11
|
+
*/
|
|
12
|
+
// export const BASE_URLS = {
|
|
13
|
+
// API: 'http://localhost:3022',
|
|
14
|
+
// AUTH: 'https://auth.heylua.ai',
|
|
15
|
+
// CHAT: 'http://localhost:3001',
|
|
16
|
+
// } as const;
|
package/dist/index.d.ts
CHANGED
|
@@ -6,15 +6,6 @@
|
|
|
6
6
|
* Provides tools for authentication, project initialization, compilation,
|
|
7
7
|
* testing, deployment, and development.
|
|
8
8
|
*
|
|
9
|
-
* Usage:
|
|
10
|
-
* lua auth configure # Set up authentication
|
|
11
|
-
* lua init # Initialize new project
|
|
12
|
-
* lua compile # Compile skill
|
|
13
|
-
* lua test # Test tools
|
|
14
|
-
* lua push # Push to server
|
|
15
|
-
* lua deploy # Deploy to production
|
|
16
|
-
* lua dev # Start development mode
|
|
17
|
-
*
|
|
18
9
|
* For more information: https://docs.heylua.ai
|
|
19
10
|
*/
|
|
20
11
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -6,15 +6,6 @@
|
|
|
6
6
|
* Provides tools for authentication, project initialization, compilation,
|
|
7
7
|
* testing, deployment, and development.
|
|
8
8
|
*
|
|
9
|
-
* Usage:
|
|
10
|
-
* lua auth configure # Set up authentication
|
|
11
|
-
* lua init # Initialize new project
|
|
12
|
-
* lua compile # Compile skill
|
|
13
|
-
* lua test # Test tools
|
|
14
|
-
* lua push # Push to server
|
|
15
|
-
* lua deploy # Deploy to production
|
|
16
|
-
* lua dev # Start development mode
|
|
17
|
-
*
|
|
18
9
|
* For more information: https://docs.heylua.ai
|
|
19
10
|
*/
|
|
20
11
|
import { Command } from "commander";
|
|
@@ -24,8 +15,36 @@ const program = new Command();
|
|
|
24
15
|
// Configure program metadata
|
|
25
16
|
program
|
|
26
17
|
.name("lua")
|
|
27
|
-
.description("Lua AI
|
|
28
|
-
.version("2.
|
|
18
|
+
.description("Lua AI - Build and deploy AI agents with superpowers")
|
|
19
|
+
.version("2.4.0")
|
|
20
|
+
.addHelpText('before', `
|
|
21
|
+
------------------------------------------------------------------
|
|
22
|
+
Lua AI CLI v2.4.0 - Build and deploy AI agents with superpowers
|
|
23
|
+
------------------------------------------------------------------
|
|
24
|
+
`)
|
|
25
|
+
.addHelpText('after', `
|
|
26
|
+
Categories:
|
|
27
|
+
🔐 Authentication Manage API keys and authentication
|
|
28
|
+
🚀 Project Setup Initialize and configure projects
|
|
29
|
+
📦 Development Build and test your skills locally
|
|
30
|
+
☁️ Deployment Push and deploy to production
|
|
31
|
+
💬 Testing Chat and test your agent
|
|
32
|
+
⚙️ Configuration Manage environment and persona
|
|
33
|
+
|
|
34
|
+
Examples:
|
|
35
|
+
$ lua auth configure 🔑 Set up your API key
|
|
36
|
+
$ lua init 🚀 Initialize a new project
|
|
37
|
+
$ lua compile 📦 Compile your skills
|
|
38
|
+
$ lua test 🧪 Test tools interactively
|
|
39
|
+
$ lua push ☁️ Push to server
|
|
40
|
+
$ lua deploy 🚀 Deploy to production
|
|
41
|
+
$ lua chat 💬 Start interactive chat
|
|
42
|
+
$ lua env ⚙️ Manage environment variables
|
|
43
|
+
$ lua persona 🤖 Manage agent persona
|
|
44
|
+
|
|
45
|
+
🌙 Documentation: https://docs.heylua.ai
|
|
46
|
+
🌙 Support: https://heylua.ai/support
|
|
47
|
+
`);
|
|
29
48
|
// Set up all command groups
|
|
30
49
|
setupAuthCommands(program);
|
|
31
50
|
setupSkillCommands(program);
|
package/dist/utils/bundling.js
CHANGED
|
@@ -8,6 +8,66 @@ import { build } from "esbuild";
|
|
|
8
8
|
import { writeProgress } from "./cli.js";
|
|
9
9
|
import { COMPILE_DIRS, COMPILE_FILES, ESBUILD_TOOL_CONFIG, ESBUILD_INDEX_CONFIG, DEFAULT_INPUT_SCHEMA, } from '../config/compile.constants.js';
|
|
10
10
|
import { wrapToolForVM, createExecuteFunction, evaluateZodSchemaToJsonSchema, } from './compile.js';
|
|
11
|
+
/**
|
|
12
|
+
* esbuild plugin to inject sandbox globals instead of requiring lua-cli
|
|
13
|
+
* This removes require("lua-cli") statements and injects the global API objects
|
|
14
|
+
* that are available in the sandbox (Product, User, Data, Baskets, Order)
|
|
15
|
+
*/
|
|
16
|
+
const sandboxGlobalsPlugin = {
|
|
17
|
+
name: 'sandbox-globals',
|
|
18
|
+
setup(build) {
|
|
19
|
+
// Only process user files, not node_modules
|
|
20
|
+
build.onLoad({ filter: /\.([jt]sx?)$/, namespace: 'file' }, async (args) => {
|
|
21
|
+
// Skip node_modules
|
|
22
|
+
if (args.path.includes('node_modules')) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
const contents = await fs.promises.readFile(args.path, 'utf8');
|
|
26
|
+
// Only transform files that import from lua-cli
|
|
27
|
+
if (!contents.includes('lua-cli')) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
// Replace lua-cli imports with global references
|
|
31
|
+
let transformedContents = contents;
|
|
32
|
+
// Replace named imports from lua-cli
|
|
33
|
+
// Match: import { Products, User, Data, etc. } from "lua-cli"
|
|
34
|
+
transformedContents = transformedContents.replace(/import\s+{([^}]+)}\s+from\s+["']lua-cli["'];?/g, (match, imports) => {
|
|
35
|
+
// Just remove the import, globals will be available in sandbox
|
|
36
|
+
return '// lua-cli imports removed - using sandbox globals';
|
|
37
|
+
});
|
|
38
|
+
// Replace lua-cli/skill imports - keep env and LuaTool as they might be needed
|
|
39
|
+
transformedContents = transformedContents.replace(/import\s+{([^}]+)}\s+from\s+["']lua-cli\/skill["'];?/g, (match, imports) => {
|
|
40
|
+
// Check if env is imported, if so we need to keep a reference
|
|
41
|
+
if (imports.includes('env')) {
|
|
42
|
+
return '// lua-cli/skill imports removed - env available in sandbox';
|
|
43
|
+
}
|
|
44
|
+
return '// lua-cli/skill imports removed - using sandbox globals';
|
|
45
|
+
});
|
|
46
|
+
// Map import names to sandbox global names
|
|
47
|
+
// Products -> Product, Orders -> Order, etc.
|
|
48
|
+
const globalMappings = {
|
|
49
|
+
'Products': 'Products',
|
|
50
|
+
'User': 'User',
|
|
51
|
+
'Data': 'Data',
|
|
52
|
+
'Baskets': 'Baskets',
|
|
53
|
+
'Orders': 'Orders'
|
|
54
|
+
};
|
|
55
|
+
// Replace usage of imported names with globals
|
|
56
|
+
for (const [importName, globalName] of Object.entries(globalMappings)) {
|
|
57
|
+
// Replace standalone usage (e.g., Products.method() -> Product.method())
|
|
58
|
+
const regex = new RegExp(`\\b${importName}\\.`, 'g');
|
|
59
|
+
transformedContents = transformedContents.replace(regex, `${globalName}.`);
|
|
60
|
+
}
|
|
61
|
+
// Note: env function and LuaTool interface are already available in sandbox
|
|
62
|
+
// env is a function in the sandbox context (see sandbox.ts line 333)
|
|
63
|
+
return {
|
|
64
|
+
contents: transformedContents,
|
|
65
|
+
loader: args.path.endsWith('.ts') ? 'ts' :
|
|
66
|
+
args.path.endsWith('.tsx') ? 'tsx' : 'js'
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
},
|
|
70
|
+
};
|
|
11
71
|
/**
|
|
12
72
|
* Bundles a tool's TypeScript code into a standalone JavaScript file.
|
|
13
73
|
* Uses esbuild to:
|
|
@@ -26,6 +86,7 @@ export async function bundleTool(tool, distDir) {
|
|
|
26
86
|
...ESBUILD_TOOL_CONFIG,
|
|
27
87
|
entryPoints: [tool.filePath],
|
|
28
88
|
outfile: outputPath,
|
|
89
|
+
plugins: [sandboxGlobalsPlugin],
|
|
29
90
|
});
|
|
30
91
|
// Wrap the bundled code for VM execution environment
|
|
31
92
|
await wrapToolForVM(outputPath, tool);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt Handler Utility
|
|
3
|
+
* Gracefully handles user interruptions (Ctrl+C) in inquirer prompts
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Wraps inquirer.prompt to gracefully handle Ctrl+C interruptions
|
|
7
|
+
*/
|
|
8
|
+
export declare function safePrompt<T = any>(questions: any): Promise<T | null>;
|
|
9
|
+
/**
|
|
10
|
+
* Setup global SIGINT handler for graceful exits
|
|
11
|
+
*/
|
|
12
|
+
export declare function setupGlobalInterruptHandler(): void;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt Handler Utility
|
|
3
|
+
* Gracefully handles user interruptions (Ctrl+C) in inquirer prompts
|
|
4
|
+
*/
|
|
5
|
+
import inquirer from 'inquirer';
|
|
6
|
+
/**
|
|
7
|
+
* Wraps inquirer.prompt to gracefully handle Ctrl+C interruptions
|
|
8
|
+
*/
|
|
9
|
+
export async function safePrompt(questions) {
|
|
10
|
+
try {
|
|
11
|
+
const answers = await inquirer.prompt(questions);
|
|
12
|
+
return answers;
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
// Handle Ctrl+C gracefully
|
|
16
|
+
if (error.name === 'ExitPromptError' || error.message?.includes('SIGINT')) {
|
|
17
|
+
console.log('\n\n👋 Operation cancelled. Goodbye!\n');
|
|
18
|
+
process.exit(0);
|
|
19
|
+
}
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Setup global SIGINT handler for graceful exits
|
|
25
|
+
*/
|
|
26
|
+
export function setupGlobalInterruptHandler() {
|
|
27
|
+
process.on('SIGINT', () => {
|
|
28
|
+
console.log('\n\n👋 Operation cancelled. Goodbye!\n');
|
|
29
|
+
process.exit(0);
|
|
30
|
+
});
|
|
31
|
+
}
|