lua-cli 2.2.8-alpha.1 → 2.3.0-alpha.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.
- package/API_REFERENCE.md +1408 -0
- package/CLI_REFERENCE.md +818 -0
- package/GETTING_STARTED.md +1040 -0
- package/README.md +738 -424
- package/TEMPLATE_GUIDE.md +1398 -0
- package/dist/api/agent.api.service.d.ts +45 -0
- package/dist/api/agent.api.service.js +57 -0
- package/dist/api/auth.api.service.d.ts +48 -0
- package/dist/api/auth.api.service.js +54 -0
- package/dist/api/basket.api.service.d.ts +85 -0
- package/dist/api/basket.api.service.js +164 -0
- package/dist/api/chat.api.service.d.ts +21 -0
- package/dist/api/chat.api.service.js +24 -0
- package/dist/api/credentials.d.ts +24 -0
- package/dist/api/credentials.js +46 -0
- package/dist/api/custom.data.api.service.d.ts +69 -0
- package/dist/api/custom.data.api.service.js +125 -0
- package/dist/api/lazy-instances.d.ts +49 -0
- package/dist/api/lazy-instances.js +95 -0
- package/dist/api/order.api.service.d.ts +53 -0
- package/dist/api/order.api.service.js +95 -0
- package/dist/api/products.api.service.d.ts +66 -0
- package/dist/api/products.api.service.js +112 -0
- package/dist/api/skills.api.service.d.ts +77 -0
- package/dist/api/skills.api.service.js +88 -0
- package/dist/api/tool.api.service.d.ts +52 -0
- package/dist/api/tool.api.service.js +73 -0
- package/dist/api/user.data.api.service.d.ts +33 -0
- package/dist/api/user.data.api.service.js +59 -0
- package/dist/api-exports.d.ts +271 -0
- package/dist/api-exports.js +372 -0
- package/dist/cli/command-definitions.d.ts +30 -0
- package/dist/cli/command-definitions.js +71 -0
- package/dist/commands/agents.d.ts +20 -0
- package/dist/commands/agents.js +24 -2
- package/dist/commands/apiKey.d.ts +23 -0
- package/dist/commands/apiKey.js +23 -0
- package/dist/commands/compile.d.ts +24 -0
- package/dist/commands/compile.js +67 -759
- package/dist/commands/configure.d.ts +24 -0
- package/dist/commands/configure.js +31 -96
- package/dist/commands/deploy.d.ts +31 -19
- package/dist/commands/deploy.js +45 -74
- package/dist/commands/destroy.d.ts +27 -0
- package/dist/commands/destroy.js +27 -1
- package/dist/commands/dev.d.ts +25 -62
- package/dist/commands/dev.js +58 -873
- package/dist/commands/init.d.ts +27 -0
- package/dist/commands/init.js +98 -260
- package/dist/commands/push.d.ts +24 -21
- package/dist/commands/push.js +39 -92
- package/dist/commands/test.d.ts +26 -0
- package/dist/commands/test.js +41 -188
- package/dist/common/basket.instance.d.ts +78 -0
- package/dist/common/basket.instance.js +132 -0
- package/dist/common/data.entry.instance.d.ts +39 -0
- package/dist/common/data.entry.instance.js +76 -0
- package/dist/common/http.client.d.ts +64 -0
- package/dist/common/http.client.js +133 -0
- package/dist/common/order.instance.d.ts +40 -0
- package/dist/common/order.instance.js +79 -0
- package/dist/common/product.instance.d.ts +33 -0
- package/dist/common/product.instance.js +63 -0
- package/dist/common/product.pagination.instance.d.ts +43 -0
- package/dist/common/product.pagination.instance.js +74 -0
- package/dist/common/product.search.instance.d.ts +22 -0
- package/dist/common/product.search.instance.js +40 -0
- package/dist/common/user.instance.d.ts +41 -0
- package/dist/common/user.instance.js +84 -0
- package/dist/config/auth.constants.d.ts +11 -0
- package/dist/config/auth.constants.js +11 -0
- package/dist/config/compile.constants.d.ts +67 -0
- package/dist/config/compile.constants.js +99 -0
- package/dist/config/constants.d.ts +5 -0
- package/dist/config/constants.js +5 -0
- package/dist/config/dev.constants.d.ts +65 -0
- package/dist/config/dev.constants.js +79 -0
- package/dist/config/init.constants.d.ts +23 -0
- package/dist/config/init.constants.js +41 -0
- package/dist/index.d.ts +19 -3
- package/dist/index.js +28 -44
- package/dist/interfaces/admin.d.ts +101 -0
- package/dist/interfaces/admin.js +5 -0
- package/dist/interfaces/agent.d.ts +107 -0
- package/dist/interfaces/agent.js +5 -0
- package/dist/interfaces/baskets.d.ts +135 -0
- package/dist/interfaces/baskets.js +19 -0
- package/dist/interfaces/chat.d.ts +61 -0
- package/dist/interfaces/chat.js +5 -0
- package/dist/interfaces/common.d.ts +62 -0
- package/dist/interfaces/common.js +8 -0
- package/dist/interfaces/compile.d.ts +11 -0
- package/dist/interfaces/compile.js +4 -0
- package/dist/interfaces/custom.data.d.ts +82 -0
- package/dist/interfaces/custom.data.js +5 -0
- package/dist/interfaces/deploy.d.ts +29 -0
- package/dist/interfaces/deploy.js +4 -0
- package/dist/interfaces/dev.d.ts +53 -0
- package/dist/interfaces/dev.js +5 -0
- package/dist/interfaces/init.d.ts +60 -0
- package/dist/interfaces/init.js +4 -0
- package/dist/interfaces/orders.d.ts +91 -0
- package/dist/interfaces/orders.js +19 -0
- package/dist/interfaces/product.d.ts +65 -0
- package/dist/interfaces/product.js +5 -0
- package/dist/interfaces/push.d.ts +26 -0
- package/dist/interfaces/push.js +4 -0
- package/dist/interfaces/test.d.ts +36 -0
- package/dist/interfaces/test.js +4 -0
- package/dist/services/auth.d.ts +54 -99
- package/dist/services/auth.js +76 -12
- package/dist/types/api-contracts.d.ts +211 -0
- package/dist/types/api-contracts.js +8 -0
- package/dist/types/compile.types.d.ts +76 -0
- package/dist/types/compile.types.js +4 -0
- package/dist/types/index.d.ts +23 -85
- package/dist/types/index.js +25 -14
- package/dist/types/skill.d.ts +142 -0
- package/dist/{skill.js → types/skill.js} +66 -19
- package/dist/types/tool-validation.d.ts +34 -0
- package/dist/types/tool-validation.js +42 -0
- package/dist/utils/auth-flows.d.ts +26 -0
- package/dist/utils/auth-flows.js +141 -0
- package/dist/utils/bundling.d.ts +36 -0
- package/dist/utils/bundling.js +137 -0
- package/dist/utils/compile.d.ts +37 -0
- package/dist/utils/compile.js +242 -0
- package/dist/utils/deploy-api.d.ts +26 -0
- package/dist/utils/deploy-api.js +53 -0
- package/dist/utils/deploy-helpers.d.ts +46 -0
- package/dist/utils/deploy-helpers.js +86 -0
- package/dist/utils/deployment.d.ts +25 -0
- package/dist/utils/deployment.js +161 -0
- package/dist/utils/dev-api.d.ts +61 -0
- package/dist/utils/dev-api.js +262 -0
- package/dist/utils/dev-helpers.d.ts +46 -0
- package/dist/utils/dev-helpers.js +83 -0
- package/dist/utils/dev-server.d.ts +24 -0
- package/dist/utils/dev-server.js +555 -0
- package/dist/utils/dev-watcher.d.ts +31 -0
- package/dist/utils/dev-watcher.js +110 -0
- package/dist/utils/files.js +0 -5
- package/dist/utils/init-agent.d.ts +34 -0
- package/dist/utils/init-agent.js +129 -0
- package/dist/utils/init-helpers.d.ts +41 -0
- package/dist/utils/init-helpers.js +73 -0
- package/dist/utils/init-prompts.d.ts +47 -0
- package/dist/utils/init-prompts.js +168 -0
- package/dist/utils/push-api.d.ts +15 -0
- package/dist/utils/push-api.js +48 -0
- package/dist/utils/push-helpers.d.ts +38 -0
- package/dist/utils/push-helpers.js +84 -0
- package/dist/utils/sandbox-storage.d.ts +27 -0
- package/dist/utils/sandbox-storage.js +71 -0
- package/dist/utils/sandbox.js +78 -118
- package/dist/utils/skill-management.d.ts +14 -0
- package/dist/utils/skill-management.js +148 -0
- package/dist/utils/test-helpers.d.ts +40 -0
- package/dist/utils/test-helpers.js +92 -0
- package/dist/utils/test-prompts.d.ts +23 -0
- package/dist/utils/test-prompts.js +186 -0
- package/dist/utils/tool-detection.d.ts +18 -0
- package/dist/utils/tool-detection.js +110 -0
- package/dist/web/app.css +14 -9
- package/package.json +11 -12
- package/template/QUICKSTART.md +299 -144
- package/template/README.md +928 -349
- package/template/TOOL_EXAMPLES.md +655 -0
- package/template/package-lock.json +3781 -0
- package/template/package.json +1 -1
- package/template/src/index.ts +81 -40
- package/template/src/tools/BasketTool.ts +128 -0
- package/template/src/tools/CustomDataTool.ts +7 -13
- package/template/src/tools/OrderTool.ts +54 -0
- package/template/src/tools/PaymentTool.ts +1 -1
- package/template/src/tools/ProductsTool.ts +56 -118
- package/template/src/tools/UserDataTool.ts +4 -27
- package/dist/custom-data-api.d.ts +0 -72
- package/dist/custom-data-api.js +0 -174
- package/dist/product-api.d.ts +0 -197
- package/dist/product-api.js +0 -152
- package/dist/services/api.d.ts +0 -569
- package/dist/services/api.js +0 -625
- package/dist/skill.d.ts +0 -50
- package/dist/types.d.ts +0 -1
- package/dist/types.js +0 -2
- package/dist/user-data-api.d.ts +0 -39
- package/dist/user-data-api.js +0 -50
- package/template/API.md +0 -604
- package/template/DEVELOPER.md +0 -771
- package/template/lua.skill.yaml +0 -16
package/dist/commands/dev.js
CHANGED
|
@@ -1,821 +1,75 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Dev Command
|
|
3
|
+
* Orchestrates the development mode with live reloading, chat interface, and tool testing
|
|
4
|
+
*/
|
|
5
|
+
import open from 'open';
|
|
4
6
|
import { compileCommand } from './compile.js';
|
|
5
7
|
import { checkApiKey, loadApiKey } from '../services/auth.js';
|
|
6
|
-
import {
|
|
7
|
-
import { executeTool, createBroadcastConsole, loadEnvironmentVariables } from '../utils/sandbox.js';
|
|
8
|
-
import { readSkillConfig, updateSkillYamlPersona } from '../utils/files.js';
|
|
8
|
+
import { readSkillConfig } from '../utils/files.js';
|
|
9
9
|
import { withErrorHandling, writeProgress, writeSuccess } from '../utils/cli.js';
|
|
10
|
-
import
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
catch (error) {
|
|
37
|
-
// Ignore storage errors
|
|
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
|
-
}
|
|
67
|
-
// Function to send chat message to API
|
|
68
|
-
export async function sendChatMessage(apiKey, agentId, skillId, sandboxId, message, persona) {
|
|
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
|
-
];
|
|
79
|
-
const chatRequest = {
|
|
80
|
-
messages: [
|
|
81
|
-
{
|
|
82
|
-
type: "text",
|
|
83
|
-
text: message
|
|
84
|
-
}
|
|
85
|
-
],
|
|
86
|
-
navigate: true,
|
|
87
|
-
skillOverride: skillOverride
|
|
88
|
-
};
|
|
89
|
-
// Add persona override if provided
|
|
90
|
-
if (persona) {
|
|
91
|
-
chatRequest.personaOverride = persona;
|
|
92
|
-
}
|
|
93
|
-
const response = await ApiService.Chat.sendMessage(agentId, chatRequest, apiKey);
|
|
94
|
-
if (!response.success) {
|
|
95
|
-
console.error(`❌ Chat API error: ${response.error?.message || 'Unknown error'}`);
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
|
-
return response.data?.text || null;
|
|
99
|
-
}
|
|
100
|
-
catch (error) {
|
|
101
|
-
console.error("❌ Error sending chat message:", error);
|
|
102
|
-
return null;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
// Function to create and start web server for chat interface
|
|
106
|
-
function createChatServer(apiKey, agentId, skillId, sandboxId, port = 3000) {
|
|
107
|
-
const server = createServer((req, res) => {
|
|
108
|
-
// Enable CORS
|
|
109
|
-
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
110
|
-
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
111
|
-
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
112
|
-
if (req.method === 'OPTIONS') {
|
|
113
|
-
res.writeHead(200);
|
|
114
|
-
res.end();
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
if (req.method === 'POST' && req.url === '/chat') {
|
|
118
|
-
let body = '';
|
|
119
|
-
req.on('data', chunk => {
|
|
120
|
-
body += chunk.toString();
|
|
121
|
-
});
|
|
122
|
-
req.on('end', async () => {
|
|
123
|
-
try {
|
|
124
|
-
const { message, persona } = JSON.parse(body);
|
|
125
|
-
const response = await sendChatMessage(apiKey, agentId, skillId, sandboxId, message, persona);
|
|
126
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
127
|
-
res.end(JSON.stringify({
|
|
128
|
-
success: response !== null,
|
|
129
|
-
text: response || 'Error sending message',
|
|
130
|
-
error: response === null ? 'Failed to send message' : null
|
|
131
|
-
}));
|
|
132
|
-
}
|
|
133
|
-
catch (error) {
|
|
134
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
135
|
-
res.end(JSON.stringify({ success: false, error: 'Invalid request' }));
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
// Persona management endpoints
|
|
141
|
-
if (req.method === 'GET' && req.url === '/persona') {
|
|
142
|
-
try {
|
|
143
|
-
const config = readSkillConfig();
|
|
144
|
-
const persona = config?.agent?.persona || '';
|
|
145
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
146
|
-
res.end(JSON.stringify({
|
|
147
|
-
success: true,
|
|
148
|
-
persona: persona
|
|
149
|
-
}));
|
|
150
|
-
}
|
|
151
|
-
catch (error) {
|
|
152
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
153
|
-
res.end(JSON.stringify({ success: false, error: 'Failed to read persona' }));
|
|
154
|
-
}
|
|
155
|
-
return;
|
|
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
|
-
}
|
|
271
|
-
if (req.method === 'POST' && req.url === '/persona') {
|
|
272
|
-
let body = '';
|
|
273
|
-
req.on('data', chunk => {
|
|
274
|
-
body += chunk.toString();
|
|
275
|
-
});
|
|
276
|
-
req.on('end', async () => {
|
|
277
|
-
try {
|
|
278
|
-
const { persona } = JSON.parse(body);
|
|
279
|
-
// Update the YAML file
|
|
280
|
-
updateSkillYamlPersona(persona);
|
|
281
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
282
|
-
res.end(JSON.stringify({
|
|
283
|
-
success: true,
|
|
284
|
-
message: 'Persona updated successfully'
|
|
285
|
-
}));
|
|
286
|
-
}
|
|
287
|
-
catch (error) {
|
|
288
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
289
|
-
res.end(JSON.stringify({ success: false, error: 'Failed to update persona' }));
|
|
290
|
-
}
|
|
291
|
-
});
|
|
292
|
-
return;
|
|
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
|
-
}
|
|
338
|
-
// Tools endpoints
|
|
339
|
-
if (req.method === 'GET' && req.url === '/tools') {
|
|
340
|
-
try {
|
|
341
|
-
// Read the deploy.json file to get tools
|
|
342
|
-
const deployPath = path.join(process.cwd(), '.lua', 'deploy.json');
|
|
343
|
-
if (fs.existsSync(deployPath)) {
|
|
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
|
-
}
|
|
358
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
359
|
-
res.end(JSON.stringify({
|
|
360
|
-
success: true,
|
|
361
|
-
tools: tools
|
|
362
|
-
}));
|
|
363
|
-
}
|
|
364
|
-
else {
|
|
365
|
-
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
366
|
-
res.end(JSON.stringify({ success: false, error: 'deploy.json not found' }));
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
catch (error) {
|
|
370
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
371
|
-
res.end(JSON.stringify({ success: false, error: 'Failed to load tools' }));
|
|
372
|
-
}
|
|
373
|
-
return;
|
|
374
|
-
}
|
|
375
|
-
if (req.method === 'POST' && req.url === '/tools/test') {
|
|
376
|
-
let body = '';
|
|
377
|
-
req.on('data', chunk => {
|
|
378
|
-
body += chunk.toString();
|
|
379
|
-
});
|
|
380
|
-
req.on('end', async () => {
|
|
381
|
-
try {
|
|
382
|
-
const { toolName, inputs } = JSON.parse(body);
|
|
383
|
-
// Read deploy.json to get the tool's execute function
|
|
384
|
-
const deployPath = path.join(process.cwd(), '.lua', 'deploy.json');
|
|
385
|
-
if (!fs.existsSync(deployPath)) {
|
|
386
|
-
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
387
|
-
res.end(JSON.stringify({ success: false, error: 'deploy.json not found' }));
|
|
388
|
-
return;
|
|
389
|
-
}
|
|
390
|
-
const deployData = JSON.parse(fs.readFileSync(deployPath, 'utf8'));
|
|
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
|
-
}
|
|
411
|
-
if (!tool) {
|
|
412
|
-
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
413
|
-
res.end(JSON.stringify({ success: false, error: 'Tool not found' }));
|
|
414
|
-
return;
|
|
415
|
-
}
|
|
416
|
-
// Load environment variables from all sources (.env + yaml)
|
|
417
|
-
const envVars = loadEnvironmentVariables();
|
|
418
|
-
// Decompress and execute the tool
|
|
419
|
-
const { gunzipSync } = await import('zlib');
|
|
420
|
-
const { Buffer } = await import('buffer');
|
|
421
|
-
const { createRequire } = await import('module');
|
|
422
|
-
const vm = await import('vm');
|
|
423
|
-
function decompressCode(compressedCode) {
|
|
424
|
-
const buffer = Buffer.from(compressedCode, 'base64');
|
|
425
|
-
return gunzipSync(buffer).toString('utf8');
|
|
426
|
-
}
|
|
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
|
-
});
|
|
440
|
-
// Create custom console that captures output and sends via WebSocket
|
|
441
|
-
const customConsole = createBroadcastConsole((logData) => {
|
|
442
|
-
console.log('Broadcasting tool log:', logData);
|
|
443
|
-
wss.clients.forEach((client) => {
|
|
444
|
-
if (client.readyState === 1) { // WebSocket.OPEN
|
|
445
|
-
client.send(JSON.stringify(logData));
|
|
446
|
-
}
|
|
447
|
-
});
|
|
448
|
-
});
|
|
449
|
-
// Execute the tool using the common utility
|
|
450
|
-
const result = await executeTool({
|
|
451
|
-
toolCode,
|
|
452
|
-
inputs,
|
|
453
|
-
apiKey,
|
|
454
|
-
agentId,
|
|
455
|
-
customConsole
|
|
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
|
-
});
|
|
469
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
470
|
-
res.end(JSON.stringify({
|
|
471
|
-
success: true,
|
|
472
|
-
result: result
|
|
473
|
-
}));
|
|
474
|
-
}
|
|
475
|
-
catch (error) {
|
|
476
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
477
|
-
res.end(JSON.stringify({
|
|
478
|
-
success: false,
|
|
479
|
-
error: error.message || 'Tool execution failed'
|
|
480
|
-
}));
|
|
481
|
-
}
|
|
482
|
-
});
|
|
483
|
-
return;
|
|
484
|
-
}
|
|
485
|
-
if (req.method === 'GET' && req.url === '/') {
|
|
486
|
-
// Read the React app HTML template from dist directory
|
|
487
|
-
const htmlTemplate = fs.readFileSync(path.join(__dirname, '..', 'web', 'index.html'), 'utf8');
|
|
488
|
-
// Replace placeholders with actual values
|
|
489
|
-
const html = htmlTemplate
|
|
490
|
-
.replace('{{API_KEY}}', apiKey)
|
|
491
|
-
.replace('{{AGENT_ID}}', agentId)
|
|
492
|
-
.replace('{{SKILL_ID}}', skillId)
|
|
493
|
-
.replace('{{SANDBOX_ID}}', sandboxId);
|
|
494
|
-
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
495
|
-
res.end(html);
|
|
496
|
-
return;
|
|
497
|
-
}
|
|
498
|
-
// Serve the React app bundle
|
|
499
|
-
if (req.method === 'GET' && req.url === '/app.js') {
|
|
500
|
-
try {
|
|
501
|
-
const appJs = fs.readFileSync(path.join(__dirname, '..', 'web', 'app.js'), 'utf8');
|
|
502
|
-
res.writeHead(200, { 'Content-Type': 'application/javascript' });
|
|
503
|
-
res.end(appJs);
|
|
504
|
-
}
|
|
505
|
-
catch (error) {
|
|
506
|
-
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
507
|
-
res.end('React app not found. Please run the build process.');
|
|
508
|
-
}
|
|
509
|
-
return;
|
|
510
|
-
}
|
|
511
|
-
// Serve the CSS files
|
|
512
|
-
if (req.method === 'GET' && req.url === '/app.css') {
|
|
513
|
-
try {
|
|
514
|
-
const appCss = fs.readFileSync(path.join(__dirname, '..', 'web', 'app.css'), 'utf8');
|
|
515
|
-
res.writeHead(200, { 'Content-Type': 'text/css' });
|
|
516
|
-
res.end(appCss);
|
|
517
|
-
}
|
|
518
|
-
catch (error) {
|
|
519
|
-
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
520
|
-
res.end('CSS not found');
|
|
521
|
-
}
|
|
522
|
-
return;
|
|
523
|
-
}
|
|
524
|
-
if (req.method === 'GET' && req.url === '/tools-page.css') {
|
|
525
|
-
try {
|
|
526
|
-
const toolsPageCss = fs.readFileSync(path.join(__dirname, '..', 'web', 'tools-page.css'), 'utf8');
|
|
527
|
-
res.writeHead(200, { 'Content-Type': 'text/css' });
|
|
528
|
-
res.end(toolsPageCss);
|
|
529
|
-
}
|
|
530
|
-
catch (error) {
|
|
531
|
-
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
532
|
-
res.end('Tools page CSS not found');
|
|
533
|
-
}
|
|
534
|
-
return;
|
|
535
|
-
}
|
|
536
|
-
res.writeHead(404);
|
|
537
|
-
res.end('Not Found');
|
|
538
|
-
});
|
|
539
|
-
// Create WebSocket server for log streaming
|
|
540
|
-
const wss = new WebSocketServer({ server });
|
|
541
|
-
wss.on('connection', (ws) => {
|
|
542
|
-
console.log('WebSocket client connected for log streaming');
|
|
543
|
-
ws.on('close', () => {
|
|
544
|
-
console.log('WebSocket client disconnected');
|
|
545
|
-
});
|
|
546
|
-
ws.on('error', (error) => {
|
|
547
|
-
console.error('WebSocket error:', error);
|
|
548
|
-
});
|
|
549
|
-
// Send initial connection message
|
|
550
|
-
ws.send(JSON.stringify({
|
|
551
|
-
type: 'log',
|
|
552
|
-
subType: 'info',
|
|
553
|
-
message: 'Connected to CLI dev server',
|
|
554
|
-
timestamp: new Date().toISOString()
|
|
555
|
-
}));
|
|
556
|
-
});
|
|
557
|
-
server.listen(port, () => {
|
|
558
|
-
console.log('Chat interface available at: http://localhost:' + port);
|
|
559
|
-
console.log('Log streaming WebSocket available at: ws://localhost:' + port);
|
|
560
|
-
});
|
|
561
|
-
// Function to broadcast logs to all connected clients
|
|
562
|
-
const broadcastLog = (message, subType = 'info') => {
|
|
563
|
-
const logMessage = {
|
|
564
|
-
type: 'log',
|
|
565
|
-
subType,
|
|
566
|
-
message,
|
|
567
|
-
timestamp: new Date().toISOString(),
|
|
568
|
-
id: Date.now().toString()
|
|
569
|
-
};
|
|
570
|
-
wss.clients.forEach((client) => {
|
|
571
|
-
if (client.readyState === 1) { // WebSocket.OPEN
|
|
572
|
-
client.send(JSON.stringify(logMessage));
|
|
573
|
-
}
|
|
574
|
-
});
|
|
575
|
-
};
|
|
576
|
-
return { server, wss, broadcastLog };
|
|
577
|
-
}
|
|
578
|
-
// Function to update existing sandbox version
|
|
579
|
-
export async function updateDevVersion(apiKey, agentId, skillId, sandboxVersionId, versionData) {
|
|
580
|
-
try {
|
|
581
|
-
// console.log('Calling UPDATE endpoint for sandbox version');
|
|
582
|
-
const response = await ApiService.Skill.updateDevSkill(apiKey, agentId, skillId, sandboxVersionId, versionData);
|
|
583
|
-
if (response.success) {
|
|
584
|
-
return {
|
|
585
|
-
success: true,
|
|
586
|
-
data: response.data
|
|
587
|
-
};
|
|
588
|
-
}
|
|
589
|
-
else {
|
|
590
|
-
return {
|
|
591
|
-
success: false,
|
|
592
|
-
error: {
|
|
593
|
-
message: response.error?.message || 'Unknown error',
|
|
594
|
-
error: response.error?.message || 'Unknown error',
|
|
595
|
-
statusCode: response.error?.statusCode || 500
|
|
596
|
-
}
|
|
597
|
-
};
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
catch (error) {
|
|
601
|
-
console.error("Network error updating dev version:", error);
|
|
602
|
-
return {
|
|
603
|
-
success: false,
|
|
604
|
-
error: {
|
|
605
|
-
message: "Network error",
|
|
606
|
-
error: error instanceof Error ? error.message : "Failed to connect to server",
|
|
607
|
-
statusCode: 500
|
|
608
|
-
}
|
|
609
|
-
};
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
// Function to create new sandbox version
|
|
613
|
-
export async function pushDevVersion(apiKey, agentId, skillId, versionData) {
|
|
614
|
-
try {
|
|
615
|
-
// console.log(`🔗 Calling CREATE endpoint for sandbox version`);
|
|
616
|
-
const response = await ApiService.Skill.pushDevSkill(apiKey, agentId, skillId, versionData);
|
|
617
|
-
if (response.success) {
|
|
618
|
-
return {
|
|
619
|
-
success: true,
|
|
620
|
-
data: response.data
|
|
621
|
-
};
|
|
622
|
-
}
|
|
623
|
-
else {
|
|
624
|
-
return {
|
|
625
|
-
success: false,
|
|
626
|
-
error: {
|
|
627
|
-
message: response.error?.message || 'Unknown error',
|
|
628
|
-
error: response.error?.message || 'Unknown error',
|
|
629
|
-
statusCode: response.error?.statusCode || 500
|
|
630
|
-
}
|
|
631
|
-
};
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
catch (error) {
|
|
635
|
-
console.error("❌ Network error pushing dev version:", error);
|
|
636
|
-
return {
|
|
637
|
-
success: false,
|
|
638
|
-
error: {
|
|
639
|
-
message: "Network error",
|
|
640
|
-
error: error instanceof Error ? error.message : "Failed to connect to server",
|
|
641
|
-
statusCode: 500
|
|
642
|
-
}
|
|
643
|
-
};
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
function readConfigVersion() {
|
|
647
|
-
const config = readSkillConfig();
|
|
648
|
-
return config?.skill?.version || null;
|
|
649
|
-
}
|
|
650
|
-
function readDeployJson() {
|
|
651
|
-
const deployPath = path.join(process.cwd(), '.lua', 'deploy.json');
|
|
652
|
-
if (!fs.existsSync(deployPath)) {
|
|
653
|
-
return null;
|
|
654
|
-
}
|
|
655
|
-
const deployContent = fs.readFileSync(deployPath, 'utf8');
|
|
656
|
-
return JSON.parse(deployContent);
|
|
657
|
-
}
|
|
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);
|
|
692
|
-
const payloadWithNameAndEnv = {
|
|
693
|
-
...skillData,
|
|
694
|
-
name: skillName,
|
|
695
|
-
env: envVars
|
|
696
|
-
};
|
|
697
|
-
if (sandboxSkillId) {
|
|
698
|
-
// Try to update existing sandbox version
|
|
699
|
-
if (!isInitial) {
|
|
700
|
-
writeProgress("🔄 Updating existing sandbox version...");
|
|
701
|
-
}
|
|
702
|
-
const updateResult = await updateDevVersion(apiKey, agentId, skillId, sandboxSkillId, payloadWithNameAndEnv);
|
|
703
|
-
if (updateResult.success && updateResult.data) {
|
|
704
|
-
if (!isInitial) {
|
|
705
|
-
// writeSuccess(`✅ Version ${updateResult.data.version} updated in sandbox successfully`);
|
|
706
|
-
// writeSuccess(`🔑 Sandbox Skill ID: ${updateResult.data.skillId}`);
|
|
707
|
-
}
|
|
708
|
-
return true;
|
|
709
|
-
}
|
|
710
|
-
else if (updateResult.error) {
|
|
711
|
-
if (!isInitial) {
|
|
712
|
-
writeProgress("⚠️ Failed to update existing sandbox, creating new one...");
|
|
713
|
-
console.error(`❌ Sandbox update failed: ${updateResult.error.message}`);
|
|
714
|
-
if (updateResult.error.error) {
|
|
715
|
-
console.error(` Details: ${updateResult.error.error}`);
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
// Fall through to create new sandbox
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
// Create new sandbox version (either no existing ID or update failed)
|
|
722
|
-
if (!isInitial) {
|
|
723
|
-
writeProgress("🔄 Creating new sandbox version...");
|
|
724
|
-
}
|
|
725
|
-
const result = await pushDevVersion(apiKey, agentId, skillId, payloadWithNameAndEnv);
|
|
726
|
-
if (result.success && result.data) {
|
|
727
|
-
// Store the new sandbox skill ID
|
|
728
|
-
await setSandboxSkillId(result.data.skillId, skillName);
|
|
729
|
-
if (!isInitial) {
|
|
730
|
-
writeSuccess(`✅ Version ${result.data.version} pushed to sandbox successfully`);
|
|
731
|
-
writeSuccess(`🔑 Sandbox Skill ID: ${result.data.skillId}`);
|
|
732
|
-
}
|
|
733
|
-
return true;
|
|
734
|
-
}
|
|
735
|
-
else if (result.error) {
|
|
736
|
-
if (!isInitial) {
|
|
737
|
-
console.error(`❌ Sandbox creation failed: ${result.error.message}`);
|
|
738
|
-
if (result.error.error) {
|
|
739
|
-
console.error(` Details: ${result.error.error}`);
|
|
740
|
-
}
|
|
741
|
-
if (result.error.statusCode) {
|
|
742
|
-
console.error(` Status Code: ${result.error.statusCode}`);
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
return false;
|
|
746
|
-
}
|
|
747
|
-
else {
|
|
748
|
-
if (!isInitial) {
|
|
749
|
-
console.error("❌ Failed to push version to sandbox. Please try again.");
|
|
750
|
-
}
|
|
751
|
-
return false;
|
|
752
|
-
}
|
|
753
|
-
}
|
|
10
|
+
import { pushSkillsToSandbox } from '../utils/dev-api.js';
|
|
11
|
+
import { createChatServer } from '../utils/dev-server.js';
|
|
12
|
+
import { createFileWatcher } from '../utils/dev-watcher.js';
|
|
13
|
+
import { readConfigVersion, readDeployJson, extractSkillId, validateConfig, validateDeployData, validateAgentConfig, } from '../utils/dev-helpers.js';
|
|
14
|
+
import { DEV_SERVER_PORT } from '../config/dev.constants.js';
|
|
15
|
+
/**
|
|
16
|
+
* Main dev command - starts development mode with live reloading.
|
|
17
|
+
*
|
|
18
|
+
* This command performs the following steps:
|
|
19
|
+
* 1. Validates configuration and authentication
|
|
20
|
+
* 2. Compiles the skill
|
|
21
|
+
* 3. Pushes all skills to sandbox environment
|
|
22
|
+
* 4. Starts web server with chat interface and tool testing
|
|
23
|
+
* 5. Opens browser to chat interface
|
|
24
|
+
* 6. Watches for file changes and auto-recompiles
|
|
25
|
+
*
|
|
26
|
+
* Features:
|
|
27
|
+
* - Live chat interface for testing skills
|
|
28
|
+
* - Real-time tool testing with WebSocket log streaming
|
|
29
|
+
* - Automatic recompilation and push on file changes
|
|
30
|
+
* - Environment variable management
|
|
31
|
+
* - Persona configuration
|
|
32
|
+
* - Multi-skill support
|
|
33
|
+
*
|
|
34
|
+
* @returns Promise that resolves when dev mode is stopped (never resolves normally)
|
|
35
|
+
*/
|
|
754
36
|
export async function devCommand() {
|
|
755
37
|
return withErrorHandling(async () => {
|
|
756
|
-
//
|
|
38
|
+
// Step 1: Validate configuration
|
|
757
39
|
const config = readSkillConfig();
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
}
|
|
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
|
|
766
|
-
}
|
|
767
|
-
if (!version) {
|
|
768
|
-
version = "1.0.0"; // Default version
|
|
769
|
-
}
|
|
770
|
-
// Load API key
|
|
40
|
+
validateConfig(config);
|
|
41
|
+
const version = readConfigVersion(config);
|
|
42
|
+
// Step 2: Authenticate
|
|
771
43
|
const apiKey = await loadApiKey();
|
|
772
44
|
if (!apiKey) {
|
|
773
45
|
console.error("❌ No API key found. Please run 'lua auth configure' to set up your API key.");
|
|
774
46
|
process.exit(1);
|
|
775
47
|
}
|
|
776
|
-
// Validate API key
|
|
777
48
|
const userData = await checkApiKey(apiKey);
|
|
778
49
|
writeProgress("✅ Authenticated");
|
|
779
|
-
// Compile the skill
|
|
50
|
+
// Step 3: Compile the skill
|
|
780
51
|
writeProgress("🔄 Compiling skill...");
|
|
781
52
|
await compileCommand();
|
|
782
|
-
//
|
|
53
|
+
// Step 4: Read and validate compiled output
|
|
783
54
|
const updatedConfig = readSkillConfig();
|
|
784
55
|
if (!updatedConfig) {
|
|
785
56
|
console.error("❌ Failed to read updated skill configuration after compilation.");
|
|
786
57
|
process.exit(1);
|
|
787
58
|
}
|
|
788
|
-
// Read deploy.json
|
|
789
59
|
const deployData = readDeployJson();
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
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.");
|
|
797
|
-
process.exit(1);
|
|
798
|
-
}
|
|
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
|
-
}
|
|
60
|
+
validateDeployData(deployData);
|
|
61
|
+
// Step 5: Extract configuration values
|
|
62
|
+
validateAgentConfig(updatedConfig);
|
|
63
|
+
const agentId = updatedConfig.agent.agentId;
|
|
64
|
+
const skillId = extractSkillId(updatedConfig);
|
|
810
65
|
if (!skillId) {
|
|
811
66
|
console.error("❌ No skillId found after compilation. Skill creation may have failed.");
|
|
812
67
|
console.error("Available skills:", updatedConfig.skills);
|
|
813
68
|
process.exit(1);
|
|
814
69
|
}
|
|
815
|
-
//
|
|
70
|
+
// Step 6: Push skills to sandbox
|
|
816
71
|
writeProgress("🔄 Pushing skills to sandbox...");
|
|
817
72
|
const sandboxIds = await pushSkillsToSandbox(apiKey, agentId, deployData, true);
|
|
818
|
-
// console.log(sandboxIds);
|
|
819
73
|
if (Object.keys(sandboxIds).length === 0) {
|
|
820
74
|
console.error("❌ Failed to push any skills to sandbox. Cannot start development mode.");
|
|
821
75
|
process.exit(1);
|
|
@@ -828,100 +82,31 @@ export async function devCommand() {
|
|
|
828
82
|
console.error("❌ No sandbox skill ID found. Cannot start chat interface.");
|
|
829
83
|
process.exit(1);
|
|
830
84
|
}
|
|
831
|
-
// Start web server for chat interface
|
|
832
|
-
const
|
|
833
|
-
|
|
834
|
-
// Open browser to chat interface
|
|
85
|
+
// Step 7: Start web server for chat interface
|
|
86
|
+
const { server, wss, broadcastLog } = createChatServer(apiKey, agentId, skillId, sandboxSkillId, DEV_SERVER_PORT);
|
|
87
|
+
// Step 8: Open browser to chat interface
|
|
835
88
|
try {
|
|
836
|
-
await open(`http://localhost:${
|
|
89
|
+
await open(`http://localhost:${DEV_SERVER_PORT}`);
|
|
837
90
|
writeSuccess("🌐 Chat interface opened in your browser");
|
|
838
91
|
broadcastLog("🌐 Chat interface opened in your browser", 'info');
|
|
839
92
|
}
|
|
840
93
|
catch (error) {
|
|
841
|
-
writeSuccess(`🌐 Chat interface available at: http://localhost:${
|
|
842
|
-
broadcastLog(`🌐 Chat interface available at: http://localhost:${
|
|
94
|
+
writeSuccess(`🌐 Chat interface available at: http://localhost:${DEV_SERVER_PORT}`);
|
|
95
|
+
broadcastLog(`🌐 Chat interface available at: http://localhost:${DEV_SERVER_PORT}`, 'info');
|
|
843
96
|
}
|
|
844
|
-
// Start file watching
|
|
97
|
+
// Step 9: Start file watching
|
|
845
98
|
writeSuccess("🔍 Watching for file changes... (Press Ctrl+C to stop)");
|
|
846
99
|
broadcastLog("🔍 Watching for file changes... (Press Ctrl+C to stop)", 'info');
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
if (filename && (filename.includes('.lua/') ||
|
|
853
|
-
filename.includes('.lua\\') ||
|
|
854
|
-
filename.includes('node_modules/') ||
|
|
855
|
-
filename.includes('node_modules\\') ||
|
|
856
|
-
filename.includes('.git/') ||
|
|
857
|
-
filename.includes('.git\\') ||
|
|
858
|
-
filename.endsWith('.log') ||
|
|
859
|
-
filename.endsWith('.tmp') ||
|
|
860
|
-
filename.endsWith('.js') ||
|
|
861
|
-
filename.endsWith('.map') ||
|
|
862
|
-
filename.includes('dist/') ||
|
|
863
|
-
filename.includes('dist\\'))) {
|
|
864
|
-
return;
|
|
865
|
-
}
|
|
866
|
-
// Debounce rapid changes
|
|
867
|
-
if (isCompiling) {
|
|
868
|
-
pendingCompile = true;
|
|
869
|
-
return;
|
|
870
|
-
}
|
|
871
|
-
// Clear any existing timeout
|
|
872
|
-
if (compileTimeout) {
|
|
873
|
-
clearTimeout(compileTimeout);
|
|
874
|
-
}
|
|
875
|
-
// Debounce file changes with a 500ms delay
|
|
876
|
-
compileTimeout = setTimeout(async () => {
|
|
877
|
-
if (isCompiling) {
|
|
878
|
-
return;
|
|
879
|
-
}
|
|
880
|
-
isCompiling = true;
|
|
881
|
-
pendingCompile = false;
|
|
882
|
-
try {
|
|
883
|
-
writeProgress(`🔄 File changed: ${filename} - Compiling and pushing...`);
|
|
884
|
-
broadcastLog(`🔄 File changed: ${filename} - Compiling and pushing...`, 'info');
|
|
885
|
-
// Compile the skill
|
|
886
|
-
await compileCommand();
|
|
887
|
-
broadcastLog("✅ Compilation completed", 'info');
|
|
888
|
-
// Read updated deploy.json
|
|
889
|
-
const updatedDeployData = readDeployJson();
|
|
890
|
-
if (!updatedDeployData) {
|
|
891
|
-
writeProgress("❌ Compilation failed, skipping push");
|
|
892
|
-
return;
|
|
893
|
-
}
|
|
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");
|
|
897
|
-
return;
|
|
898
|
-
}
|
|
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');
|
|
904
|
-
}
|
|
905
|
-
else {
|
|
906
|
-
// writeProgress(`✅ Successfully pushed ${Object.keys(updatedSandboxIds).length} skills to sandbox`);
|
|
907
|
-
broadcastLog(`✅ Successfully pushed ${Object.keys(updatedSandboxIds).length} skills to sandbox`, 'info');
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
catch (error) {
|
|
911
|
-
writeProgress(`❌ Error during auto-compile: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
912
|
-
}
|
|
913
|
-
finally {
|
|
914
|
-
isCompiling = false;
|
|
915
|
-
}
|
|
916
|
-
}, 500); // 500ms debounce delay
|
|
100
|
+
const { watcher, cleanup } = createFileWatcher({
|
|
101
|
+
apiKey,
|
|
102
|
+
agentId,
|
|
103
|
+
readDeployJson,
|
|
104
|
+
broadcastLog,
|
|
917
105
|
});
|
|
918
|
-
// Handle graceful shutdown
|
|
106
|
+
// Step 10: Handle graceful shutdown
|
|
919
107
|
process.on('SIGINT', () => {
|
|
920
108
|
writeSuccess("\n🛑 Stopping file watcher...");
|
|
921
|
-
|
|
922
|
-
clearTimeout(compileTimeout);
|
|
923
|
-
}
|
|
924
|
-
watcher.close();
|
|
109
|
+
cleanup();
|
|
925
110
|
process.exit(0);
|
|
926
111
|
});
|
|
927
112
|
// Keep the process alive
|