@nestbox-ai/cli 1.0.38 → 1.0.39
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/.github/workflows/test.yml +54 -0
- package/dist/commands/agent/apiUtils.d.ts +20 -0
- package/dist/commands/agent/apiUtils.js +75 -2
- package/dist/commands/agent/apiUtils.js.map +1 -1
- package/dist/commands/agent/create.d.ts +2 -29
- package/dist/commands/agent/create.js +123 -61
- package/dist/commands/agent/create.js.map +1 -1
- package/dist/commands/agent/deploy.js +183 -137
- package/dist/commands/agent/deploy.js.map +1 -1
- package/dist/commands/agent/index.d.ts +1 -2
- package/dist/commands/agent/index.js +3 -6
- package/dist/commands/agent/index.js.map +1 -1
- package/dist/commands/agent/yaml-schema.d.ts +72 -0
- package/dist/commands/agent/yaml-schema.js +61 -0
- package/dist/commands/agent/yaml-schema.js.map +1 -0
- package/dist/commands/agent.js +2 -2
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/generate/project.js +32 -28
- package/dist/commands/generate/project.js.map +1 -1
- package/dist/utils/agent.js +18 -20
- package/dist/utils/agent.js.map +1 -1
- package/package.json +3 -2
- package/src/commands/agent/apiUtils.ts +103 -14
- package/src/commands/agent/create.ts +266 -100
- package/src/commands/agent/deploy.ts +515 -264
- package/src/commands/agent/index.ts +1 -4
- package/src/commands/agent/yaml-schema.ts +57 -0
- package/src/commands/agent.ts +10 -10
- package/src/commands/generate/project.ts +179 -147
- package/src/utils/agent.ts +141 -125
- package/test/agent.test.ts +153 -124
- package/dist/commands/agent/createFromYaml.d.ts +0 -2
- package/dist/commands/agent/createFromYaml.js +0 -172
- package/dist/commands/agent/createFromYaml.js.map +0 -1
- package/src/commands/agent/createFromYaml.ts +0 -192
package/src/utils/agent.ts
CHANGED
|
@@ -1,154 +1,170 @@
|
|
|
1
1
|
import path from "path";
|
|
2
|
-
import fs from
|
|
2
|
+
import fs from "fs";
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import ora from "ora";
|
|
5
5
|
import { promisify } from "util";
|
|
6
6
|
import { exec } from "child_process";
|
|
7
7
|
import AdmZip from "adm-zip";
|
|
8
|
-
import * as os from
|
|
8
|
+
import * as os from "os";
|
|
9
9
|
import axios from "axios";
|
|
10
10
|
|
|
11
|
-
|
|
12
11
|
const execAsync = promisify(exec);
|
|
13
12
|
|
|
14
13
|
export async function findProjectRoot(startDir = process.cwd()) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
14
|
+
let currentDir = startDir;
|
|
15
|
+
|
|
16
|
+
while (currentDir !== path.parse(currentDir).root) {
|
|
17
|
+
const nestboxConfigPath = path.join(currentDir, "nestbox.config.json");
|
|
18
|
+
const packageJsonPath = path.join(currentDir, "package.json");
|
|
19
|
+
|
|
20
|
+
if (
|
|
21
|
+
fs.existsSync(nestboxConfigPath) ||
|
|
22
|
+
fs.existsSync(packageJsonPath)
|
|
23
|
+
) {
|
|
24
|
+
return currentDir;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
currentDir = path.dirname(currentDir);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return startDir; // Fallback to current directory if no root markers found
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
// Function to load and parse nestbox.config.json if it exists
|
|
32
34
|
export function loadNestboxConfig(projectRoot: any) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
35
|
+
const configPath = path.join(projectRoot, "nestbox.config.json");
|
|
36
|
+
|
|
37
|
+
if (fs.existsSync(configPath)) {
|
|
38
|
+
try {
|
|
39
|
+
const configContent = fs.readFileSync(configPath, "utf8");
|
|
40
|
+
return JSON.parse(configContent);
|
|
41
|
+
} catch (error: any) {
|
|
42
|
+
console.warn(
|
|
43
|
+
chalk.yellow(
|
|
44
|
+
`Warning: Error parsing nestbox.config.json: ${error.message}`
|
|
45
|
+
)
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return null;
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
// Function to detect if a directory contains TypeScript files
|
|
48
54
|
export function isTypeScriptProject(directoryPath: any) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
// Check for tsconfig.json
|
|
56
|
+
if (fs.existsSync(path.join(directoryPath, "tsconfig.json"))) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Check for .ts files
|
|
61
|
+
try {
|
|
62
|
+
const files = fs.readdirSync(directoryPath);
|
|
63
|
+
return files.some(
|
|
64
|
+
file => file.endsWith(".ts") || file.endsWith(".tsx")
|
|
65
|
+
);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
61
69
|
}
|
|
62
70
|
|
|
63
71
|
export async function runPredeployScripts(scripts: any, projectRoot: any) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
72
|
+
if (!scripts || !Array.isArray(scripts) || scripts.length === 0) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const spinner = ora("Running predeploy scripts...").start();
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
for (const script of scripts) {
|
|
80
|
+
spinner.text = `Running: ${script}`;
|
|
81
|
+
|
|
82
|
+
// Make sure we're running in the correct directory
|
|
83
|
+
await execAsync(script, {
|
|
84
|
+
cwd: projectRoot,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
spinner.succeed("Predeploy scripts completed successfully");
|
|
88
|
+
} catch (error: any) {
|
|
89
|
+
spinner.fail(`Predeploy script failed: ${error.message}`);
|
|
90
|
+
throw new Error(`Predeploy failed: ${error.message}`);
|
|
91
|
+
}
|
|
84
92
|
}
|
|
85
93
|
|
|
86
|
-
export function createZipFromDirectory(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
94
|
+
export function createZipFromDirectory(
|
|
95
|
+
dirPath: any,
|
|
96
|
+
excludePatterns = ["node_modules"]
|
|
97
|
+
) {
|
|
98
|
+
const dirName = path.basename(dirPath);
|
|
99
|
+
const timestamp = Date.now();
|
|
100
|
+
|
|
101
|
+
// Create zip in temp directory
|
|
102
|
+
const tempZipFilePath = path.join(
|
|
103
|
+
os.tmpdir(),
|
|
104
|
+
`${dirName}_${timestamp}.zip`
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
const zip = new AdmZip();
|
|
108
|
+
|
|
109
|
+
// Function to recursively add files to zip
|
|
110
|
+
function addFilesToZip(currentPath: any, relativePath = "") {
|
|
111
|
+
const items = fs.readdirSync(currentPath);
|
|
112
|
+
|
|
113
|
+
for (const item of items) {
|
|
114
|
+
const itemPath = path.join(currentPath, item);
|
|
115
|
+
const itemRelativePath = path.join(relativePath, item);
|
|
116
|
+
|
|
117
|
+
// Check if item should be excluded
|
|
118
|
+
if (
|
|
119
|
+
excludePatterns.some((pattern: any) =>
|
|
120
|
+
typeof pattern === "string"
|
|
121
|
+
? itemRelativePath === pattern || item === pattern
|
|
122
|
+
: pattern.test(itemRelativePath)
|
|
123
|
+
)
|
|
124
|
+
) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const stats = fs.statSync(itemPath);
|
|
129
|
+
|
|
130
|
+
if (stats.isDirectory()) {
|
|
131
|
+
addFilesToZip(itemPath, itemRelativePath);
|
|
132
|
+
} else {
|
|
133
|
+
zip.addLocalFile(itemPath, path.dirname(itemRelativePath));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
addFilesToZip(dirPath);
|
|
139
|
+
|
|
140
|
+
// Write zip to temp directory (for upload)
|
|
141
|
+
zip.writeZip(tempZipFilePath);
|
|
142
|
+
|
|
143
|
+
// Return the temp path for upload
|
|
144
|
+
return tempZipFilePath;
|
|
128
145
|
}
|
|
129
146
|
|
|
130
147
|
export interface TemplateInfo {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
148
|
+
name: string;
|
|
149
|
+
description: string;
|
|
150
|
+
fileId: string;
|
|
151
|
+
lang: string;
|
|
152
|
+
type: string;
|
|
136
153
|
}
|
|
137
154
|
|
|
138
|
-
export function createNestboxConfig(
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
+
export function createNestboxConfig(
|
|
156
|
+
projectPath: string,
|
|
157
|
+
isTypeScript: boolean
|
|
158
|
+
): void {
|
|
159
|
+
if (!isTypeScript) return;
|
|
160
|
+
|
|
161
|
+
const configPath = path.join(projectPath, "nestbox.config.json");
|
|
162
|
+
const config = {
|
|
163
|
+
agents: {
|
|
164
|
+
predeploy: ["rm -rf dist", "npm run lint", "npm run build"],
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
169
|
+
console.log(chalk.green(`Created nestbox.config.json at ${configPath}`));
|
|
170
|
+
}
|
package/test/agent.test.ts
CHANGED
|
@@ -1,125 +1,154 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from
|
|
2
|
-
import { Command } from
|
|
3
|
-
import { registerAgentCommands } from
|
|
4
|
-
|
|
5
|
-
describe(
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { registerAgentCommands } from "../src/commands/agent";
|
|
4
|
+
|
|
5
|
+
describe("Agent Commands", () => {
|
|
6
|
+
let program: Command;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
program = new Command();
|
|
10
|
+
vi.clearAllMocks();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
describe("registerAgentCommands", () => {
|
|
14
|
+
it("should register agent command group", () => {
|
|
15
|
+
registerAgentCommands(program);
|
|
16
|
+
|
|
17
|
+
const commands = program.commands;
|
|
18
|
+
const agentCommand = commands.find(cmd => cmd.name() === "agent");
|
|
19
|
+
|
|
20
|
+
expect(agentCommand).toBeDefined();
|
|
21
|
+
expect(agentCommand?.description()).toBe("Manage Nestbox agents");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("should register agent list subcommand", () => {
|
|
25
|
+
registerAgentCommands(program);
|
|
26
|
+
|
|
27
|
+
const agentCommand = program.commands.find(
|
|
28
|
+
cmd => cmd.name() === "agent"
|
|
29
|
+
);
|
|
30
|
+
const subCommands = agentCommand?.commands || [];
|
|
31
|
+
const listCommand = subCommands.find(cmd => cmd.name() === "list");
|
|
32
|
+
|
|
33
|
+
expect(listCommand).toBeDefined();
|
|
34
|
+
expect(listCommand?.description()).toBe(
|
|
35
|
+
"List all AI agents associated with the authenticated user"
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// Check options
|
|
39
|
+
const options = listCommand?.options || [];
|
|
40
|
+
const projectOption = options.find(opt => opt.long === "--project");
|
|
41
|
+
expect(projectOption).toBeDefined();
|
|
42
|
+
expect(projectOption?.description).toBe(
|
|
43
|
+
"Project name (defaults to the current project)"
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("should register agent remove subcommand", () => {
|
|
48
|
+
registerAgentCommands(program);
|
|
49
|
+
|
|
50
|
+
const agentCommand = program.commands.find(
|
|
51
|
+
cmd => cmd.name() === "agent"
|
|
52
|
+
);
|
|
53
|
+
const subCommands = agentCommand?.commands || [];
|
|
54
|
+
const removeCommand = subCommands.find(
|
|
55
|
+
cmd => cmd.name() === "remove"
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
expect(removeCommand).toBeDefined();
|
|
59
|
+
expect(removeCommand?.description()).toBe("Remove an AI agent");
|
|
60
|
+
|
|
61
|
+
// Check options
|
|
62
|
+
const options = removeCommand?.options || [];
|
|
63
|
+
const agentOption = options.find(opt => opt.long === "--agent");
|
|
64
|
+
const projectOption = options.find(opt => opt.long === "--project");
|
|
65
|
+
|
|
66
|
+
expect(agentOption).toBeDefined();
|
|
67
|
+
expect(projectOption).toBeDefined();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("should register agent deploy subcommand", () => {
|
|
71
|
+
registerAgentCommands(program);
|
|
72
|
+
|
|
73
|
+
const agentCommand = program.commands.find(
|
|
74
|
+
cmd => cmd.name() === "agent"
|
|
75
|
+
);
|
|
76
|
+
const subCommands = agentCommand?.commands || [];
|
|
77
|
+
const deployCommand = subCommands.find(
|
|
78
|
+
cmd => cmd.name() === "deploy"
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
expect(deployCommand).toBeDefined();
|
|
82
|
+
expect(deployCommand?.description()).toBe(
|
|
83
|
+
"Deploy an AI agent to the Nestbox platform"
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
// Check options
|
|
87
|
+
const options = deployCommand?.options || [];
|
|
88
|
+
const projectOption = options.find(opt => opt.long === "--project");
|
|
89
|
+
const agentOption = options.find(opt => opt.long === "--agent");
|
|
90
|
+
const instanceOption = options.find(
|
|
91
|
+
opt => opt.long === "--instance"
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
expect(projectOption).toBeDefined();
|
|
95
|
+
expect(agentOption).toBeDefined();
|
|
96
|
+
expect(instanceOption).toBeDefined();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("should register agent create subcommand", () => {
|
|
100
|
+
registerAgentCommands(program);
|
|
101
|
+
|
|
102
|
+
const agentCommand = program.commands.find(
|
|
103
|
+
cmd => cmd.name() === "agent"
|
|
104
|
+
);
|
|
105
|
+
const subCommands = agentCommand?.commands || [];
|
|
106
|
+
const createCommand = subCommands.find(
|
|
107
|
+
cmd => cmd.name() === "create"
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
expect(createCommand).toBeDefined();
|
|
111
|
+
expect(createCommand?.description()).toBe(
|
|
112
|
+
"Create an agent with direct arguments or YAML."
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
// Check that it has optional arguments (in command name: "create [firstArg] [secondArg]")
|
|
116
|
+
expect(createCommand?.name()).toBe("create");
|
|
117
|
+
|
|
118
|
+
// Check options
|
|
119
|
+
const options = createCommand?.options || [];
|
|
120
|
+
const projectOption = options.find(opt => opt.long === "--project");
|
|
121
|
+
|
|
122
|
+
expect(projectOption).toBeDefined();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("should have all expected agent subcommands", () => {
|
|
126
|
+
registerAgentCommands(program);
|
|
127
|
+
|
|
128
|
+
const agentCommand = program.commands.find(
|
|
129
|
+
cmd => cmd.name() === "agent"
|
|
130
|
+
);
|
|
131
|
+
const subCommandNames =
|
|
132
|
+
agentCommand?.commands.map(cmd => cmd.name()) || [];
|
|
133
|
+
|
|
134
|
+
expect(subCommandNames).toContain("list");
|
|
135
|
+
expect(subCommandNames).toContain("remove");
|
|
136
|
+
expect(subCommandNames).toContain("deploy");
|
|
137
|
+
expect(subCommandNames).toContain("create");
|
|
138
|
+
expect(subCommandNames).toHaveLength(4);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("should have proper action functions for all subcommands", () => {
|
|
142
|
+
registerAgentCommands(program);
|
|
143
|
+
|
|
144
|
+
const agentCommand = program.commands.find(
|
|
145
|
+
cmd => cmd.name() === "agent"
|
|
146
|
+
);
|
|
147
|
+
const subCommands = agentCommand?.commands || [];
|
|
148
|
+
|
|
149
|
+
subCommands.forEach(cmd => {
|
|
150
|
+
expect(typeof cmd.action).toBe("function");
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
});
|
|
125
154
|
});
|