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