@nestbox-ai/cli 1.0.6 → 1.0.8
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/.nestboxrc +0 -0
- package/dist/commands/agent.d.ts +2 -0
- package/dist/commands/agent.js +308 -0
- package/dist/commands/agent.js.map +1 -0
- package/dist/commands/auth.js +23 -2
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/compute.js +350 -94
- package/dist/commands/compute.js.map +1 -1
- package/dist/commands/document.d.ts +2 -0
- package/dist/commands/document.js +340 -0
- package/dist/commands/document.js.map +1 -0
- package/dist/commands/image.d.ts +2 -0
- package/dist/commands/image.js +147 -0
- package/dist/commands/image.js.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/agent.d.ts +5 -0
- package/dist/utils/agent.js +192 -0
- package/dist/utils/agent.js.map +1 -0
- package/dist/utils/project.d.ts +15 -0
- package/dist/utils/project.js +76 -0
- package/dist/utils/project.js.map +1 -0
- package/dist/utils/user.d.ts +1 -0
- package/dist/utils/user.js +40 -0
- package/dist/utils/user.js.map +1 -0
- package/nestbox.config.json +9 -0
- package/package.json +12 -3
- package/src/commands/agent.ts +355 -0
- package/src/commands/auth.ts +261 -238
- package/src/commands/compute.ts +407 -115
- package/src/commands/document.ts +472 -0
- package/src/commands/image.ts +155 -0
- package/src/index.ts +6 -0
- package/src/utils/agent.ts +170 -0
- package/src/utils/project.ts +81 -0
- package/src/utils/user.ts +28 -0
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import Table from 'cli-table3';
|
|
5
|
+
import { getAuthToken } from '../utils/auth';
|
|
6
|
+
import { Configuration, MachineAgentApi, ProjectsApi } from '@nestbox-ai/admin';
|
|
7
|
+
import { resolveProject } from '../utils/project';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import { createZipFromDirectory, findProjectRoot, isTypeScriptProject, loadNestboxConfig, runPredeployScripts } from '../utils/agent';
|
|
10
|
+
import axios from 'axios';
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
export function registerAgentCommands(program: Command): void {
|
|
15
|
+
// Get authentication token and create API configuration
|
|
16
|
+
const authToken = getAuthToken();
|
|
17
|
+
const configuration = new Configuration({
|
|
18
|
+
basePath: authToken?.serverUrl,
|
|
19
|
+
baseOptions: {
|
|
20
|
+
headers: {
|
|
21
|
+
"Authorization": authToken?.token,
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const agentsApi = new MachineAgentApi(configuration);
|
|
27
|
+
const projectsApi = new ProjectsApi(configuration);
|
|
28
|
+
|
|
29
|
+
// Create the main agent command
|
|
30
|
+
const agentCommand = program
|
|
31
|
+
.command('agent')
|
|
32
|
+
.description('Manage Nestbox agents');
|
|
33
|
+
|
|
34
|
+
// Add the list subcommand
|
|
35
|
+
agentCommand
|
|
36
|
+
.command('list')
|
|
37
|
+
.description('List all AI agents associated with the authenticated user')
|
|
38
|
+
.option('--project <projectName>', 'Project name (defaults to the current project)')
|
|
39
|
+
.action(async (options) => {
|
|
40
|
+
try {
|
|
41
|
+
if (!authToken) {
|
|
42
|
+
console.error(chalk.red('No authentication token found. Please login first.'));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
// Use the resolveProject helper to get project information
|
|
48
|
+
const projectData = await resolveProject(projectsApi, options);
|
|
49
|
+
|
|
50
|
+
const spinner = ora(`Listing agents in project ${projectData.name}...`).start();
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
// Now get the agents for the specific project
|
|
54
|
+
const agentsResponse: any = await agentsApi.machineAgentControllerGetMachineAgentByProjectId(projectData.id, 0, 10, "");
|
|
55
|
+
|
|
56
|
+
spinner.succeed('Successfully retrieved agents');
|
|
57
|
+
|
|
58
|
+
// Display the results
|
|
59
|
+
const agents = agentsResponse.data?.machineAgents || [];
|
|
60
|
+
|
|
61
|
+
if (!agents || agents.length === 0) {
|
|
62
|
+
console.log(chalk.yellow(`No agents found in project ${projectData.name}`));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
console.log(chalk.blue(`\nAgents in project ${projectData.name}:\n`));
|
|
67
|
+
|
|
68
|
+
// Create a formatted table focusing on id, name, and URL
|
|
69
|
+
const table = new Table({
|
|
70
|
+
head: [
|
|
71
|
+
chalk.white.bold('ID'),
|
|
72
|
+
chalk.white.bold('Name'),
|
|
73
|
+
chalk.white.bold('Goal'),
|
|
74
|
+
chalk.white.bold('URL'),
|
|
75
|
+
chalk.white.bold('Created At')
|
|
76
|
+
],
|
|
77
|
+
style: {
|
|
78
|
+
head: [], // Disable the default styling
|
|
79
|
+
border: []
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Add agents to the table with the requested info
|
|
84
|
+
agents.forEach((agent: any) => {
|
|
85
|
+
// Format the agent URL
|
|
86
|
+
let url = 'N/A';
|
|
87
|
+
if (agent.instanceIP) {
|
|
88
|
+
// Construct an agent-specific URL if possible
|
|
89
|
+
url = `${agent.instanceIP}`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Format date for readability
|
|
93
|
+
let createdAt = agent.createdAt || 'N/A';
|
|
94
|
+
if (createdAt !== 'N/A') {
|
|
95
|
+
createdAt = new Date(createdAt).toLocaleString();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
table.push([
|
|
99
|
+
agent.id || 'N/A',
|
|
100
|
+
agent.agentName || 'N/A',
|
|
101
|
+
agent.goal || 'N/A',
|
|
102
|
+
url,
|
|
103
|
+
createdAt
|
|
104
|
+
]);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Display the table
|
|
108
|
+
console.log(table.toString());
|
|
109
|
+
|
|
110
|
+
// Display totals
|
|
111
|
+
console.log(`\nTotal agents: ${agents.length}`);
|
|
112
|
+
|
|
113
|
+
} catch (error: any) {
|
|
114
|
+
spinner.fail('Failed to retrieve agents');
|
|
115
|
+
if (error.response) {
|
|
116
|
+
console.error(chalk.red('API Error:'), error.response.data?.message || 'Unknown error');
|
|
117
|
+
} else {
|
|
118
|
+
console.error(chalk.red('Error:'), error.message || 'Unknown error');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
} catch (error: any) {
|
|
122
|
+
console.error(chalk.red('Error:'), error instanceof Error ? error.message : 'Unknown error');
|
|
123
|
+
}
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.error(chalk.red('Error:'), error instanceof Error ? error.message : 'Unknown error');
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
// Remove agent
|
|
131
|
+
agentCommand
|
|
132
|
+
.command('remove')
|
|
133
|
+
.description('Remove an AI agent')
|
|
134
|
+
.requiredOption('--agent <agentId>', 'Agent ID to remove')
|
|
135
|
+
.option('--project <projectName>', 'Project name (defaults to the current project)')
|
|
136
|
+
.action(async (options) => {
|
|
137
|
+
try {
|
|
138
|
+
if (!authToken) {
|
|
139
|
+
console.error(chalk.red('No authentication token found. Please login first.'));
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const { agent } = options;
|
|
144
|
+
|
|
145
|
+
// Use the resolveProject helper to get project information
|
|
146
|
+
const projectData = await resolveProject(projectsApi, options);
|
|
147
|
+
|
|
148
|
+
const spinner = ora(`Finding agent ${agent} in project ${projectData.name}...`).start();
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
// First, get the list of agents to find the correct modelbaseId
|
|
152
|
+
const agentsResponse: any = await agentsApi.machineAgentControllerGetMachineAgentByProjectId(
|
|
153
|
+
projectData.id,
|
|
154
|
+
0,
|
|
155
|
+
100, // Increased to make sure we get all agents
|
|
156
|
+
""
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
// Get the agents array
|
|
160
|
+
const agents = agentsResponse.data?.machineAgents || [];
|
|
161
|
+
|
|
162
|
+
// Find the specific agent by ID
|
|
163
|
+
const targetAgent = agents.find((a: any) => a.id.toString() === agent.toString());
|
|
164
|
+
|
|
165
|
+
if (!targetAgent) {
|
|
166
|
+
spinner.fail(`Agent with ID ${agent} not found in project ${projectData.name}`);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Extract the modelbaseId from the found agent
|
|
171
|
+
const modelbaseId = targetAgent.modelBaseId;
|
|
172
|
+
|
|
173
|
+
if (!modelbaseId) {
|
|
174
|
+
spinner.fail(`Could not find modelbaseId for agent ${agent}. Please try again.`);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
spinner.text = `Removing agent ${agent} from project ${projectData.name}...`;
|
|
179
|
+
|
|
180
|
+
// Now remove the agent with the dynamically retrieved modelbaseId
|
|
181
|
+
const payload: any = [{
|
|
182
|
+
id: parseInt(agent, 10),
|
|
183
|
+
modelbaseId: modelbaseId
|
|
184
|
+
}];
|
|
185
|
+
|
|
186
|
+
const removeResponse = await agentsApi.machineAgentControllerDeleteMachineAgents(
|
|
187
|
+
projectData.id,
|
|
188
|
+
agent,
|
|
189
|
+
payload
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
spinner.succeed('Successfully removed agent');
|
|
193
|
+
|
|
194
|
+
// Display the results
|
|
195
|
+
console.log(chalk.green(`Agent ${agent} removed successfully from project ${projectData.name}`));
|
|
196
|
+
|
|
197
|
+
} catch (error: any) {
|
|
198
|
+
spinner.fail('Failed to remove agent');
|
|
199
|
+
if (error.response) {
|
|
200
|
+
console.error(chalk.red('API Error:'), error.response.data?.message || 'Unknown error');
|
|
201
|
+
} else {
|
|
202
|
+
console.error(chalk.red('Error:'), error.message || 'Unknown error');
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
} catch (error) {
|
|
206
|
+
console.error(chalk.red('Error:'), error instanceof Error ? error.message : 'Unknown error');
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
agentCommand
|
|
211
|
+
.command('deploy')
|
|
212
|
+
.description('Deploy an AI agent to the Nestbox platform')
|
|
213
|
+
.requiredOption('--agent <agentId>', 'Agent ID to deploy')
|
|
214
|
+
.requiredOption('--instance <instanceId>', 'Instance ID')
|
|
215
|
+
.requiredOption('--zip <zipFileOrDirPath>', 'Path to the zip file or directory to upload')
|
|
216
|
+
.option('--project <projectName>', 'Project name (defaults to the current project)')
|
|
217
|
+
.option('--entry <entryFunction>', 'Entry function name', 'main')
|
|
218
|
+
.action(async (options) => {
|
|
219
|
+
try {
|
|
220
|
+
if (!authToken) {
|
|
221
|
+
console.error(chalk.red('No authentication token found. Please login first.'));
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const { agent: agentId, instance: instanceId, zip: customZipPath, entry } = options;
|
|
226
|
+
|
|
227
|
+
// Find project root (CLI tools directory)
|
|
228
|
+
const projectRoot = await findProjectRoot();
|
|
229
|
+
console.log(chalk.blue(`Project root detected at: ${projectRoot}`));
|
|
230
|
+
|
|
231
|
+
// Use the resolveProject helper to get project information
|
|
232
|
+
const projectData = await resolveProject(projectsApi, options);
|
|
233
|
+
|
|
234
|
+
// Load nestbox.config.json from CLI tools directory
|
|
235
|
+
const config = loadNestboxConfig(projectRoot);
|
|
236
|
+
|
|
237
|
+
// Start the deployment process
|
|
238
|
+
const spinner = ora(`Preparing to deploy agent ${agentId} to instance ${instanceId}...`).start();
|
|
239
|
+
|
|
240
|
+
try {
|
|
241
|
+
// Determine the source path (custom path or project root)
|
|
242
|
+
const sourcePath = customZipPath || projectRoot;
|
|
243
|
+
|
|
244
|
+
let zipFilePath;
|
|
245
|
+
|
|
246
|
+
// Check if the specified path exists
|
|
247
|
+
if (!fs.existsSync(sourcePath)) {
|
|
248
|
+
spinner.fail(`Path not found: ${sourcePath}`);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Check if the path is a zip file or directory
|
|
253
|
+
const stats = fs.statSync(sourcePath);
|
|
254
|
+
|
|
255
|
+
if (stats.isFile()) {
|
|
256
|
+
// Case 1: It's a file - verify it's a zip and use directly
|
|
257
|
+
if (!sourcePath.toLowerCase().endsWith('.zip')) {
|
|
258
|
+
spinner.fail(`File is not a zip archive: ${sourcePath}`);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Use the zip file directly
|
|
263
|
+
spinner.text = `Using provided zip file: ${sourcePath}`;
|
|
264
|
+
zipFilePath = sourcePath;
|
|
265
|
+
|
|
266
|
+
} else if (stats.isDirectory()) {
|
|
267
|
+
// Case 2: It's a directory - check for predeploy scripts in CLI config
|
|
268
|
+
|
|
269
|
+
// Determine if it's a TypeScript project
|
|
270
|
+
const isTypeScript = isTypeScriptProject(sourcePath);
|
|
271
|
+
|
|
272
|
+
if (isTypeScript) {
|
|
273
|
+
spinner.text = `TypeScript project detected. Checking for predeploy scripts...`;
|
|
274
|
+
|
|
275
|
+
// Run predeploy scripts if defined in CLI tools config
|
|
276
|
+
if (config?.agent?.predeploy || config?.agents?.predeploy) {
|
|
277
|
+
const predeployScripts = config?.agent?.predeploy || config?.agents?.predeploy;
|
|
278
|
+
spinner.text = `Running predeploy scripts on target directory...`;
|
|
279
|
+
await runPredeployScripts(predeployScripts, sourcePath);
|
|
280
|
+
} else {
|
|
281
|
+
spinner.info('No predeploy scripts found in CLI tools nestbox.config.json');
|
|
282
|
+
}
|
|
283
|
+
} else {
|
|
284
|
+
// JavaScript directory - just zip it
|
|
285
|
+
spinner.text = `JavaScript project detected. Skipping predeploy scripts.`;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Create zip archive with node_modules excluded
|
|
289
|
+
spinner.text = `Creating zip archive from directory ${sourcePath}...`;
|
|
290
|
+
zipFilePath = createZipFromDirectory(sourcePath);
|
|
291
|
+
spinner.text = `Directory zipped successfully to ${zipFilePath}`;
|
|
292
|
+
} else {
|
|
293
|
+
spinner.fail(`Unsupported file type: ${sourcePath}`);
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
spinner.text = `Deploying agent ${agentId} to instance ${instanceId}...`;
|
|
298
|
+
|
|
299
|
+
// Clean the base URL to avoid path duplication
|
|
300
|
+
const baseUrl = authToken?.serverUrl?.endsWith('/')
|
|
301
|
+
? authToken.serverUrl.slice(0, -1)
|
|
302
|
+
: authToken?.serverUrl;
|
|
303
|
+
|
|
304
|
+
const FormData = require('form-data');
|
|
305
|
+
const form = new FormData();
|
|
306
|
+
|
|
307
|
+
// Add file as a readable stream
|
|
308
|
+
form.append('file', fs.createReadStream(zipFilePath));
|
|
309
|
+
|
|
310
|
+
// Add all the required fields
|
|
311
|
+
form.append('machineAgentId', agentId.toString());
|
|
312
|
+
form.append('instanceId', instanceId.toString());
|
|
313
|
+
form.append('entryFunctionName', entry);
|
|
314
|
+
form.append('isSourceCodeUpdate', 'true');
|
|
315
|
+
form.append('projectId', projectData.id);
|
|
316
|
+
|
|
317
|
+
// Create a custom axios instance with form-data headers
|
|
318
|
+
const axiosInstance = axios.create({
|
|
319
|
+
baseURL: baseUrl,
|
|
320
|
+
headers: {
|
|
321
|
+
...form.getHeaders(),
|
|
322
|
+
"Authorization": authToken?.token
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
// Construct the endpoint URL
|
|
327
|
+
const endpoint = `/projects/${projectData.id}/agents/${agentId}`;
|
|
328
|
+
|
|
329
|
+
// Make direct axios request
|
|
330
|
+
await axiosInstance.patch(endpoint, form);
|
|
331
|
+
|
|
332
|
+
// Clean up temporary zip file if we created one
|
|
333
|
+
if (zipFilePath !== sourcePath && fs.existsSync(zipFilePath)) {
|
|
334
|
+
fs.unlinkSync(zipFilePath);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
spinner.succeed(`Successfully deployed agent ${agentId} to instance ${instanceId}`);
|
|
338
|
+
console.log(chalk.green('Agent deployed successfully'));
|
|
339
|
+
|
|
340
|
+
} catch (error: any) {
|
|
341
|
+
spinner.fail('Failed to deploy agent');
|
|
342
|
+
|
|
343
|
+
if (error.response) {
|
|
344
|
+
console.error(chalk.red(`API Error (${error.response.status}): ${error.response.data?.message || 'Unknown error'}`));
|
|
345
|
+
} else {
|
|
346
|
+
console.error(chalk.red('Error:'), error.message || 'Unknown error');
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
} catch (error) {
|
|
350
|
+
console.error(chalk.red('Error:'), error instanceof Error ? error.message : 'Unknown error');
|
|
351
|
+
}
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
}
|