@nestbox-ai/cli 1.0.23 → 1.0.25
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/commands/agent/apiUtils.d.ts +10 -0
- package/dist/commands/agent/apiUtils.js +20 -0
- package/dist/commands/agent/apiUtils.js.map +1 -0
- package/dist/commands/agent/create.d.ts +29 -0
- package/dist/commands/agent/create.js +88 -0
- package/dist/commands/agent/create.js.map +1 -0
- package/dist/commands/agent/createFromYaml.d.ts +2 -0
- package/dist/commands/agent/createFromYaml.js +172 -0
- package/dist/commands/agent/createFromYaml.js.map +1 -0
- package/dist/commands/agent/deploy.d.ts +2 -0
- package/dist/commands/agent/deploy.js +243 -0
- package/dist/commands/agent/deploy.js.map +1 -0
- package/dist/commands/agent/generate.d.ts +2 -0
- package/dist/commands/agent/generate.js +141 -0
- package/dist/commands/agent/generate.js.map +1 -0
- package/dist/commands/agent/index.d.ts +7 -0
- package/dist/commands/agent/index.js +21 -0
- package/dist/commands/agent/index.js.map +1 -0
- package/dist/commands/agent/list.d.ts +2 -0
- package/dist/commands/agent/list.js +94 -0
- package/dist/commands/agent/list.js.map +1 -0
- package/dist/commands/agent/remove.d.ts +2 -0
- package/dist/commands/agent/remove.js +85 -0
- package/dist/commands/agent/remove.js.map +1 -0
- package/dist/commands/agent.d.ts +4 -0
- package/dist/commands/agent.js +16 -731
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/compute/apiUtils.d.ts +11 -0
- package/dist/commands/compute/apiUtils.js +22 -0
- package/dist/commands/compute/apiUtils.js.map +1 -0
- package/dist/commands/compute/create.d.ts +2 -0
- package/dist/commands/compute/create.js +183 -0
- package/dist/commands/compute/create.js.map +1 -0
- package/dist/commands/compute/delete.d.ts +2 -0
- package/dist/commands/compute/delete.js +145 -0
- package/dist/commands/compute/delete.js.map +1 -0
- package/dist/commands/compute/index.d.ts +4 -0
- package/dist/commands/compute/index.js +14 -0
- package/dist/commands/compute/index.js.map +1 -0
- package/dist/commands/compute/list.d.ts +2 -0
- package/dist/commands/compute/list.js +128 -0
- package/dist/commands/compute/list.js.map +1 -0
- package/dist/commands/compute.d.ts +3 -0
- package/dist/commands/compute.js +10 -400
- package/dist/commands/compute.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/agent/apiUtils.ts +24 -0
- package/src/commands/agent/create.ts +105 -0
- package/src/commands/agent/createFromYaml.ts +192 -0
- package/src/commands/agent/deploy.ts +272 -0
- package/src/commands/agent/generate.ts +151 -0
- package/src/commands/agent/index.ts +12 -0
- package/src/commands/agent/list.ts +104 -0
- package/src/commands/agent/remove.ts +103 -0
- package/src/commands/agent.ts +15 -909
- package/src/commands/compute/apiUtils.ts +28 -0
- package/src/commands/compute/create.ts +195 -0
- package/src/commands/compute/delete.ts +147 -0
- package/src/commands/compute/index.ts +7 -0
- package/src/commands/compute/list.ts +125 -0
- package/src/commands/compute.ts +16 -449
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { withTokenRefresh } from "../../utils/error";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import ora from "ora";
|
|
5
|
+
import Table from "cli-table3";
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import yaml from 'js-yaml';
|
|
8
|
+
import { AgentYamlConfig } from "../../types/agentYaml";
|
|
9
|
+
import { userData } from "../../utils/user";
|
|
10
|
+
import { createAgent } from "./create";
|
|
11
|
+
import { createApis } from "./apiUtils";
|
|
12
|
+
|
|
13
|
+
export function registerCreateFromYamlCommand(agentCommand: Command): void {
|
|
14
|
+
agentCommand
|
|
15
|
+
.command("create [firstArg] [secondArg]")
|
|
16
|
+
.description("Create multiple agents from a YAML configuration file")
|
|
17
|
+
.option("--project <projectId>", "Project ID (defaults to the current project)")
|
|
18
|
+
.action(async (firstArg: string, secondArg: any, options: any) => {
|
|
19
|
+
try {
|
|
20
|
+
let apis = createApis();
|
|
21
|
+
|
|
22
|
+
// Determine which argument is the YAML file path
|
|
23
|
+
let yamlFilePath: string;
|
|
24
|
+
|
|
25
|
+
if (firstArg === 'file' && secondArg) {
|
|
26
|
+
yamlFilePath = secondArg;
|
|
27
|
+
} else if (firstArg) {
|
|
28
|
+
yamlFilePath = firstArg;
|
|
29
|
+
if (typeof secondArg === 'object' && !options) {
|
|
30
|
+
options = secondArg;
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
console.error(chalk.red("Missing YAML file path. Usage: nestbox agent create <yamlFile> OR nestbox agent create file <yamlFile>"));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Check if file exists
|
|
38
|
+
if (!fs.existsSync(yamlFilePath)) {
|
|
39
|
+
console.error(chalk.red(`YAML file not found: ${yamlFilePath}`));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Read and parse the YAML file
|
|
44
|
+
const spinner = ora(`Reading agents configuration from ${yamlFilePath}...`).start();
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
const fileContents = fs.readFileSync(yamlFilePath, 'utf8');
|
|
48
|
+
const config = yaml.load(fileContents) as AgentYamlConfig;
|
|
49
|
+
|
|
50
|
+
if (!config || !config.agents || !Array.isArray(config.agents)) {
|
|
51
|
+
spinner.fail("Invalid YAML configuration: Missing 'agents' array");
|
|
52
|
+
console.error(chalk.red("The YAML file should contain an 'agents' array with agent configurations"));
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
spinner.succeed(`Found ${config.agents.length} agents in configuration file`);
|
|
57
|
+
|
|
58
|
+
// Process each agent with token refresh support
|
|
59
|
+
const results = {
|
|
60
|
+
success: 0,
|
|
61
|
+
failed: 0,
|
|
62
|
+
agents: [] as Array<{name: string; success: boolean; message: string}>
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Get user data once
|
|
66
|
+
const user = await userData();
|
|
67
|
+
|
|
68
|
+
for (const agent of config.agents) {
|
|
69
|
+
if (!agent.name) {
|
|
70
|
+
console.log(chalk.yellow("Skipping agent with no name defined"));
|
|
71
|
+
results.failed++;
|
|
72
|
+
results.agents.push({
|
|
73
|
+
name: "unnamed",
|
|
74
|
+
success: false,
|
|
75
|
+
message: "Name is required"
|
|
76
|
+
});
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let agentType = agent.type || "CHAT";
|
|
81
|
+
const resourceType = agentType === "AGENT" ? "Agent" : "Chatbot";
|
|
82
|
+
|
|
83
|
+
const agentSpinner = ora(`Creating ${resourceType.toLowerCase()} '${agent.name}'...`).start();
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
// Create agent with token refresh support
|
|
87
|
+
await withTokenRefresh(
|
|
88
|
+
async () => {
|
|
89
|
+
// Map YAML config to createAgent options
|
|
90
|
+
const createOptions = {
|
|
91
|
+
...options,
|
|
92
|
+
goal: agent.goal || "No goal specified",
|
|
93
|
+
modelBaseId: agent.modelBaseId || "",
|
|
94
|
+
instanceIP: agent.instanceIP || "localhost",
|
|
95
|
+
machineInstanceId: agent.machineInstanceId || 1,
|
|
96
|
+
machineManifestId: agent.machineManifestId || "default",
|
|
97
|
+
machineName: agent.machineName || `agent-${agent.name.toLowerCase()}`,
|
|
98
|
+
type: agentType,
|
|
99
|
+
userId: user.id,
|
|
100
|
+
parameters: agent.parameters ? agent.parameters.map((p: any) => {
|
|
101
|
+
return {
|
|
102
|
+
name: p.name || "unnamed",
|
|
103
|
+
description: p.description || "",
|
|
104
|
+
default: p.default || "",
|
|
105
|
+
isUserParam: p.isUserParam !== undefined ? p.isUserParam : true
|
|
106
|
+
};
|
|
107
|
+
}) : []
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
await createAgent(agent.name, createOptions, apis.agentsApi, apis.projectsApi);
|
|
111
|
+
},
|
|
112
|
+
() => {
|
|
113
|
+
apis = createApis();
|
|
114
|
+
}
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
agentSpinner.stop();
|
|
118
|
+
|
|
119
|
+
results.success++;
|
|
120
|
+
results.agents.push({
|
|
121
|
+
name: agent.name,
|
|
122
|
+
success: true,
|
|
123
|
+
message: `Created successfully`
|
|
124
|
+
});
|
|
125
|
+
} catch (error: any) {
|
|
126
|
+
agentSpinner.fail(`Failed to create ${resourceType.toLowerCase()} '${agent.name}'`);
|
|
127
|
+
console.error(chalk.red(`Error: ${error.message}`));
|
|
128
|
+
results.failed++;
|
|
129
|
+
results.agents.push({
|
|
130
|
+
name: agent.name,
|
|
131
|
+
success: false,
|
|
132
|
+
message: error.message
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Final summary
|
|
138
|
+
console.log(chalk.blue("\nResource creation summary:"));
|
|
139
|
+
const table = new Table({
|
|
140
|
+
head: [
|
|
141
|
+
chalk.white.bold("Name"),
|
|
142
|
+
chalk.white.bold("Type"),
|
|
143
|
+
chalk.white.bold("Status"),
|
|
144
|
+
chalk.white.bold("Message"),
|
|
145
|
+
],
|
|
146
|
+
style: {
|
|
147
|
+
head: [],
|
|
148
|
+
border: [],
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
results.agents.forEach((agent, index) => {
|
|
153
|
+
const agentConfig = config.agents.find(a => a.name === agent.name) || config.agents[index];
|
|
154
|
+
const agentType = agentConfig?.type || "CHAT";
|
|
155
|
+
const resourceType = agentType === "AGENT" ? "Agent" : "Chatbot";
|
|
156
|
+
|
|
157
|
+
table.push([
|
|
158
|
+
agent.name,
|
|
159
|
+
resourceType,
|
|
160
|
+
agent.success ? chalk.green("Success") : chalk.red("Failed"),
|
|
161
|
+
agent.message
|
|
162
|
+
]);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
console.log(table.toString());
|
|
166
|
+
console.log(`\nTotal: ${results.success + results.failed}, Successful: ${results.success}, Failed: ${results.failed}`);
|
|
167
|
+
|
|
168
|
+
} catch (error: any) {
|
|
169
|
+
spinner.fail("Failed to process YAML file");
|
|
170
|
+
if (error.code === 'ENOENT') {
|
|
171
|
+
console.error(chalk.red(`File not found: ${yamlFilePath}`));
|
|
172
|
+
} else if (error.name === 'YAMLException') {
|
|
173
|
+
console.error(chalk.red(`Invalid YAML format: ${error.message}`));
|
|
174
|
+
} else {
|
|
175
|
+
console.error(
|
|
176
|
+
chalk.red("Error:"),
|
|
177
|
+
error.message || "Unknown error"
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
} catch (error: any) {
|
|
182
|
+
if (error.message && error.message.includes('Authentication')) {
|
|
183
|
+
console.error(chalk.red(error.message));
|
|
184
|
+
} else {
|
|
185
|
+
console.error(
|
|
186
|
+
chalk.red("Error:"),
|
|
187
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { withTokenRefresh } from "../../utils/error";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import ora from "ora";
|
|
5
|
+
import { resolveProject } from "../../utils/project";
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import {
|
|
8
|
+
createZipFromDirectory,
|
|
9
|
+
findProjectRoot,
|
|
10
|
+
isTypeScriptProject,
|
|
11
|
+
loadNestboxConfig,
|
|
12
|
+
runPredeployScripts,
|
|
13
|
+
} from "../../utils/agent";
|
|
14
|
+
import axios from "axios";
|
|
15
|
+
import { AgentType } from "../../types/agentType";
|
|
16
|
+
import path from "path";
|
|
17
|
+
import { getAuthToken } from "../../utils/auth";
|
|
18
|
+
import { createApis } from "./apiUtils";
|
|
19
|
+
|
|
20
|
+
export function registerDeployCommand(agentCommand: Command): void {
|
|
21
|
+
agentCommand
|
|
22
|
+
.command("deploy")
|
|
23
|
+
.description("Deploy an AI agent to the Nestbox platform")
|
|
24
|
+
.option("--agent <agentName>", "Agent name to deploy")
|
|
25
|
+
.option("--chatbot <chatbotName>", "Chatbot name to deploy")
|
|
26
|
+
.requiredOption("--instance <instanceName>", "Instance name")
|
|
27
|
+
.option(
|
|
28
|
+
"--zip <zipFileOrDirPath>",
|
|
29
|
+
"Path to the zip file or directory to upload"
|
|
30
|
+
)
|
|
31
|
+
.option(
|
|
32
|
+
"--project <projectName>",
|
|
33
|
+
"Project name (defaults to the current project)"
|
|
34
|
+
)
|
|
35
|
+
.option("--entry <entryFunction>", "Entry function name")
|
|
36
|
+
.option("--log", "Show detailed logs during deployment")
|
|
37
|
+
.action(async (options) => {
|
|
38
|
+
try {
|
|
39
|
+
const {
|
|
40
|
+
agent: agentName,
|
|
41
|
+
chatbot: chatbotName,
|
|
42
|
+
instance: instanceName,
|
|
43
|
+
zip: customZipPath,
|
|
44
|
+
entry,
|
|
45
|
+
log,
|
|
46
|
+
} = options;
|
|
47
|
+
|
|
48
|
+
// Ensure either agent or chatbot is provided, but not both
|
|
49
|
+
if ((!agentName && !chatbotName) || (agentName && chatbotName)) {
|
|
50
|
+
console.error(
|
|
51
|
+
chalk.red("Please provide either --agent OR --chatbot option, but not both.")
|
|
52
|
+
);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let apis = createApis();
|
|
57
|
+
|
|
58
|
+
// Find project root
|
|
59
|
+
const projectRoot = await findProjectRoot();
|
|
60
|
+
console.log(chalk.blue(`Project root detected at: ${projectRoot}`));
|
|
61
|
+
|
|
62
|
+
// Main deployment logic with token refresh
|
|
63
|
+
await withTokenRefresh(
|
|
64
|
+
async () => {
|
|
65
|
+
// Resolve project
|
|
66
|
+
const projectData = await resolveProject(apis.projectsApi, options);
|
|
67
|
+
|
|
68
|
+
// Determine if we're deploying an agent or chatbot
|
|
69
|
+
const isAgent = !!agentName;
|
|
70
|
+
const resourceName = isAgent ? agentName : chatbotName;
|
|
71
|
+
const resourceType = isAgent ? "Agent" : "Chatbot";
|
|
72
|
+
const agentType = isAgent ? AgentType.REGULAR : "CHAT";
|
|
73
|
+
|
|
74
|
+
// Get agents data and find agent/chatbot by name
|
|
75
|
+
const agentsData: any = await apis.agentsApi.machineAgentControllerGetMachineAgentByProjectId(
|
|
76
|
+
projectData.id,
|
|
77
|
+
0,
|
|
78
|
+
10,
|
|
79
|
+
agentType
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const targetAgent = agentsData.data.machineAgents.find(
|
|
83
|
+
(agent: any) => agent.agentName === resourceName
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
if (!targetAgent) {
|
|
87
|
+
console.error(
|
|
88
|
+
chalk.red(`${resourceType} with name "${resourceName}" not found in project "${projectData.name}".`)
|
|
89
|
+
);
|
|
90
|
+
console.log(chalk.yellow(`Available ${resourceType.toLowerCase()}s:`));
|
|
91
|
+
agentsData.data.machineAgents.forEach((agent: any) => {
|
|
92
|
+
console.log(chalk.yellow(` - ${agent.agentName} (ID: ${agent.id})`));
|
|
93
|
+
});
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Get instance data and find instance by name
|
|
98
|
+
const instanceData: any = await apis.instanceApi.machineInstancesControllerGetMachineInstanceByUserId(
|
|
99
|
+
projectData.id,
|
|
100
|
+
0,
|
|
101
|
+
10
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
const targetInstance = instanceData.data.machineInstances.find(
|
|
105
|
+
(instance: any) => instance.instanceName === instanceName
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
if (!targetInstance) {
|
|
109
|
+
console.error(
|
|
110
|
+
chalk.red(`Instance with name "${instanceName}" not found in project "${projectData.name}".`)
|
|
111
|
+
);
|
|
112
|
+
console.log(chalk.yellow("Available instances:"));
|
|
113
|
+
instanceData.data.machineInstances.forEach((instance: any) => {
|
|
114
|
+
console.log(chalk.yellow(` - ${instance.instanceName} (ID: ${instance.id})`));
|
|
115
|
+
});
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Extract IDs
|
|
120
|
+
const agentId = targetAgent.id;
|
|
121
|
+
const resolvedEntry = entry || targetAgent.entryFunctionName || "main";
|
|
122
|
+
const instanceId = targetInstance.id;
|
|
123
|
+
|
|
124
|
+
// Load nestbox.config.json
|
|
125
|
+
const config = loadNestboxConfig(projectRoot);
|
|
126
|
+
|
|
127
|
+
// Start the deployment process
|
|
128
|
+
const spinner = ora(
|
|
129
|
+
`Preparing to deploy ${resourceType.toLowerCase()} ${agentId} to instance ${instanceId}...`
|
|
130
|
+
).start();
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
let zipFilePath;
|
|
134
|
+
|
|
135
|
+
if (customZipPath) {
|
|
136
|
+
// Process custom zip path
|
|
137
|
+
if (!fs.existsSync(customZipPath)) {
|
|
138
|
+
spinner.fail(`Path not found: ${customZipPath}`);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const stats = fs.statSync(customZipPath);
|
|
143
|
+
|
|
144
|
+
if (stats.isFile()) {
|
|
145
|
+
if (!customZipPath.toLowerCase().endsWith(".zip")) {
|
|
146
|
+
spinner.fail(`File is not a zip archive: ${customZipPath}`);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
spinner.text = `Using provided zip file: ${customZipPath}`;
|
|
150
|
+
zipFilePath = customZipPath;
|
|
151
|
+
} else if (stats.isDirectory()) {
|
|
152
|
+
// Process directory
|
|
153
|
+
spinner.text = `Processing directory: ${customZipPath}`;
|
|
154
|
+
|
|
155
|
+
const isTypeScript = isTypeScriptProject(customZipPath);
|
|
156
|
+
|
|
157
|
+
if (isTypeScript && (config?.agent?.predeploy || config?.agents?.predeploy)) {
|
|
158
|
+
const predeployScripts = config?.agent?.predeploy || config?.agents?.predeploy;
|
|
159
|
+
spinner.text = `Running predeploy scripts on target directory...`;
|
|
160
|
+
await runPredeployScripts(predeployScripts, customZipPath);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
spinner.text = `Creating zip archive from directory ${customZipPath}...`;
|
|
164
|
+
zipFilePath = createZipFromDirectory(customZipPath);
|
|
165
|
+
spinner.text = `Directory zipped successfully to ${zipFilePath}`;
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
// Use project root
|
|
169
|
+
spinner.text = `Using project root: ${projectRoot}`;
|
|
170
|
+
|
|
171
|
+
const isTypeScript = isTypeScriptProject(projectRoot);
|
|
172
|
+
|
|
173
|
+
if (isTypeScript && (config?.agent?.predeploy || config?.agents?.predeploy)) {
|
|
174
|
+
const predeployScripts = config?.agent?.predeploy || config?.agents?.predeploy;
|
|
175
|
+
spinner.text = `Running predeploy scripts on project root...`;
|
|
176
|
+
await runPredeployScripts(predeployScripts, projectRoot);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
spinner.text = `Creating zip archive from project root ${projectRoot}...`;
|
|
180
|
+
zipFilePath = createZipFromDirectory(projectRoot);
|
|
181
|
+
spinner.text = `Directory zipped successfully to ${zipFilePath}`;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
spinner.text = `Deploying ${resourceType.toLowerCase()} ${agentId} to instance ${instanceId}...`;
|
|
185
|
+
|
|
186
|
+
// Prepare deployment
|
|
187
|
+
const authToken = getAuthToken();
|
|
188
|
+
const baseUrl = authToken?.serverUrl?.endsWith("/")
|
|
189
|
+
? authToken.serverUrl.slice(0, -1)
|
|
190
|
+
: authToken?.serverUrl;
|
|
191
|
+
|
|
192
|
+
const { default: FormData } = await import("form-data");
|
|
193
|
+
const form = new FormData();
|
|
194
|
+
|
|
195
|
+
form.append("file", fs.createReadStream(zipFilePath));
|
|
196
|
+
form.append("machineAgentId", agentId.toString());
|
|
197
|
+
form.append("instanceId", instanceId.toString());
|
|
198
|
+
form.append("entryFunctionName", resolvedEntry);
|
|
199
|
+
form.append("isSourceCodeUpdate", "true");
|
|
200
|
+
form.append("projectId", projectData.id);
|
|
201
|
+
|
|
202
|
+
if (log) {
|
|
203
|
+
console.log(chalk.blue("Form Details "));
|
|
204
|
+
console.log(chalk.blue(` - File: ${path.basename(zipFilePath)}`));
|
|
205
|
+
console.log(chalk.blue(` - Agent ID: ${agentId}`));
|
|
206
|
+
console.log(chalk.blue(` - Instance ID: ${instanceId}`));
|
|
207
|
+
console.log(chalk.blue(` - Entry Function: ${resolvedEntry}`));
|
|
208
|
+
console.log(chalk.blue(` - Project ID: ${projectData.id}`));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const axiosInstance = axios.create({
|
|
212
|
+
baseURL: baseUrl,
|
|
213
|
+
headers: {
|
|
214
|
+
...form.getHeaders(),
|
|
215
|
+
Authorization: authToken?.token,
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
const endpoint = `/projects/${projectData.id}/agents/${agentId}`;
|
|
220
|
+
|
|
221
|
+
spinner.text = `Sending API request to deploy ${resourceType.toLowerCase()}...`;
|
|
222
|
+
const res = await axiosInstance.patch(endpoint, form);
|
|
223
|
+
|
|
224
|
+
if (!customZipPath && zipFilePath && fs.existsSync(zipFilePath)) {
|
|
225
|
+
fs.unlinkSync(zipFilePath);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (log) {
|
|
229
|
+
console.log(chalk.blue("\nDeployment request:"));
|
|
230
|
+
console.log(chalk.blue(` URL: ${baseUrl}${endpoint}`));
|
|
231
|
+
console.log(chalk.blue(` Method: PATCH`));
|
|
232
|
+
console.log(chalk.blue(` File: ${path.basename(zipFilePath)}`));
|
|
233
|
+
console.log(chalk.blue(` Response status: ${res.status} ${res.statusText}`));
|
|
234
|
+
const lines = res.data.logEntries || [];
|
|
235
|
+
console.log(chalk.blue(` Deployment log entries (${lines.length} lines):`));
|
|
236
|
+
lines.forEach((line: any) => {
|
|
237
|
+
console.log(chalk.blue(` - [${line.type} ${line.timestamp}] ${line.message} `));
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
spinner.succeed("Successfully deployed");
|
|
241
|
+
console.log(chalk.green(`${resourceType} deployed successfully!`));
|
|
242
|
+
console.log(chalk.cyan(`📍 Instance: ${instanceName}`));
|
|
243
|
+
console.log(chalk.cyan(`🤖 Agent: ${agentName} (${agentId})`));
|
|
244
|
+
console.log(chalk.cyan(`⚙️ Entry: ${resolvedEntry}`));
|
|
245
|
+
console.log(chalk.cyan(`🔄 Process: ${res.data.processName}`));
|
|
246
|
+
} catch (error: any) {
|
|
247
|
+
spinner.fail(`Failed to deploy ${resourceType.toLowerCase()}`);
|
|
248
|
+
throw error;
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
() => {
|
|
252
|
+
apis = createApis();
|
|
253
|
+
}
|
|
254
|
+
);
|
|
255
|
+
} catch (error: any) {
|
|
256
|
+
if (error.message && error.message.includes('Authentication')) {
|
|
257
|
+
console.error(chalk.red(error.message));
|
|
258
|
+
} else if (error.response) {
|
|
259
|
+
console.error(
|
|
260
|
+
chalk.red(
|
|
261
|
+
`API Error (${error.response.status}): ${error.response.data?.message || "Unknown error"}`
|
|
262
|
+
)
|
|
263
|
+
);
|
|
264
|
+
if (error.response.data) {
|
|
265
|
+
console.error(chalk.red(`Error Data: ${JSON.stringify(error.response.data, null, 2)}`));
|
|
266
|
+
}
|
|
267
|
+
} else {
|
|
268
|
+
console.error(chalk.red("Error:"), error.message || "Unknown error");
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import ora from "ora";
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import {
|
|
6
|
+
createNestboxConfig,
|
|
7
|
+
extractZip,
|
|
8
|
+
} from "../../utils/agent";
|
|
9
|
+
import inquirer from "inquirer";
|
|
10
|
+
import path from "path";
|
|
11
|
+
|
|
12
|
+
export function registerGenerateCommand(agentCommand: Command): void {
|
|
13
|
+
agentCommand
|
|
14
|
+
.command("generate <folder>")
|
|
15
|
+
.description("Generate a new project from templates")
|
|
16
|
+
.option("--lang <language>", "Project language (ts|js)")
|
|
17
|
+
.option("--template <type>", "Template type (agent|chatbot)")
|
|
18
|
+
.option("--project <projectId>", "Project ID")
|
|
19
|
+
.action(async (folder, options) => {
|
|
20
|
+
try {
|
|
21
|
+
const spinner = ora("Initializing project generation...").start();
|
|
22
|
+
|
|
23
|
+
// Ensure target folder doesn't exist
|
|
24
|
+
if (fs.existsSync(folder)) {
|
|
25
|
+
spinner.fail(`Folder ${folder} already exists`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let selectedLang = options.lang;
|
|
30
|
+
let selectedTemplate = options.template;
|
|
31
|
+
|
|
32
|
+
// Interactive selection if not provided
|
|
33
|
+
if (!selectedLang || !selectedTemplate) {
|
|
34
|
+
spinner.stop();
|
|
35
|
+
|
|
36
|
+
const answers = await inquirer.prompt([
|
|
37
|
+
{
|
|
38
|
+
type: 'list',
|
|
39
|
+
name: 'lang',
|
|
40
|
+
message: 'Select project language:',
|
|
41
|
+
choices: [
|
|
42
|
+
{ name: 'TypeScript', value: 'ts' },
|
|
43
|
+
{ name: 'JavaScript', value: 'js' }
|
|
44
|
+
],
|
|
45
|
+
when: () => !selectedLang
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
type: 'list',
|
|
49
|
+
name: 'template',
|
|
50
|
+
message: 'Select template type:',
|
|
51
|
+
choices: [
|
|
52
|
+
{ name: 'Agent', value: 'agent' },
|
|
53
|
+
{ name: 'Chatbot', value: 'chatbot' }
|
|
54
|
+
],
|
|
55
|
+
when: () => !selectedTemplate
|
|
56
|
+
}
|
|
57
|
+
]);
|
|
58
|
+
|
|
59
|
+
selectedLang = selectedLang || answers.lang;
|
|
60
|
+
selectedTemplate = selectedTemplate || answers.template;
|
|
61
|
+
|
|
62
|
+
spinner.start("Generating project...");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Find matching template in local templates folder
|
|
66
|
+
const templateMapping: Record<string, string> = {
|
|
67
|
+
'agent': 'base',
|
|
68
|
+
'chatbot': 'chatbot'
|
|
69
|
+
};
|
|
70
|
+
const mappedTemplateType = templateMapping[selectedTemplate] || selectedTemplate;
|
|
71
|
+
const templateKey = `template-${mappedTemplateType}-${selectedLang}.zip`;
|
|
72
|
+
|
|
73
|
+
// Try process.cwd() first, then __dirname fallback
|
|
74
|
+
let templatePath = path.resolve(process.cwd(), 'templates', templateKey);
|
|
75
|
+
if (!fs.existsSync(templatePath)) {
|
|
76
|
+
// fallback to __dirname
|
|
77
|
+
templatePath = path.resolve(__dirname, '../../../templates', templateKey);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!fs.existsSync(templatePath)) {
|
|
81
|
+
spinner.fail(`Template not found: ${templatePath}`);
|
|
82
|
+
// Show available templates in both locations
|
|
83
|
+
const cwdTemplates = path.resolve(process.cwd(), 'templates');
|
|
84
|
+
const dirTemplates = path.resolve(__dirname, '../../../templates');
|
|
85
|
+
let shown = false;
|
|
86
|
+
|
|
87
|
+
if (fs.existsSync(cwdTemplates)) {
|
|
88
|
+
console.log(chalk.yellow('Available templates in ./templates:'));
|
|
89
|
+
fs.readdirSync(cwdTemplates).forEach(file => {
|
|
90
|
+
console.log(chalk.yellow(` - ${file}`));
|
|
91
|
+
});
|
|
92
|
+
shown = true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (fs.existsSync(dirTemplates)) {
|
|
96
|
+
console.log(chalk.yellow('Available templates in templates:'));
|
|
97
|
+
fs.readdirSync(dirTemplates).forEach(file => {
|
|
98
|
+
console.log(chalk.yellow(` - ${file}`));
|
|
99
|
+
});
|
|
100
|
+
shown = true;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!shown) {
|
|
104
|
+
console.log(chalk.red('No templates directory found. Please add your templates.'));
|
|
105
|
+
}
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
spinner.text = `Extracting template to ${folder}...`;
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
// Extract template to target folder
|
|
113
|
+
extractZip(templatePath, folder);
|
|
114
|
+
|
|
115
|
+
// Create nestbox.config.json for TypeScript projects
|
|
116
|
+
createNestboxConfig(folder, selectedLang === 'ts');
|
|
117
|
+
|
|
118
|
+
// Update package.json with project name if it exists
|
|
119
|
+
const packageJsonPath = path.join(folder, 'package.json');
|
|
120
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
121
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
122
|
+
packageJson.name = path.basename(folder);
|
|
123
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
spinner.succeed(`Successfully generated ${mappedTemplateType} project in ${folder}`);
|
|
127
|
+
|
|
128
|
+
console.log(chalk.green("\nNext steps:"));
|
|
129
|
+
console.log(chalk.yellow(` cd ${folder}`));
|
|
130
|
+
console.log(chalk.yellow(" npm install"));
|
|
131
|
+
if (selectedLang === 'ts') {
|
|
132
|
+
console.log(chalk.yellow(" npm run build"));
|
|
133
|
+
}
|
|
134
|
+
console.log(chalk.yellow(" nestbox agent deploy --agent <agent-name> --instance <instance-name>"));
|
|
135
|
+
|
|
136
|
+
} catch (error) {
|
|
137
|
+
// Clean up on error
|
|
138
|
+
if (fs.existsSync(folder)) {
|
|
139
|
+
fs.rmSync(folder, { recursive: true, force: true });
|
|
140
|
+
}
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
} catch (error: any) {
|
|
145
|
+
console.error(
|
|
146
|
+
chalk.red("Error:"),
|
|
147
|
+
error.message || "Failed to generate project"
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Main agent command exports
|
|
2
|
+
export { createAgent, type CreateAgentOptions } from "./create";
|
|
3
|
+
|
|
4
|
+
// Individual command exports for direct usage if needed
|
|
5
|
+
export { registerListCommand } from "./list";
|
|
6
|
+
export { registerRemoveCommand } from "./remove";
|
|
7
|
+
export { registerDeployCommand } from "./deploy";
|
|
8
|
+
export { registerGenerateCommand } from "./generate";
|
|
9
|
+
export { registerCreateFromYamlCommand } from "./createFromYaml";
|
|
10
|
+
|
|
11
|
+
// API utilities
|
|
12
|
+
export { createApis, type ApiInstances } from "./apiUtils";
|