@nestbox-ai/cli 1.0.2 → 1.0.3

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.
@@ -0,0 +1,139 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import { getAuthToken } from '../utils/auth';
6
+ import { Configuration, ProjectsApi } from '@nestbox-ai/admin';
7
+
8
+ // Define a type for the projects configuration
9
+ interface ProjectsConfig {
10
+ default?: string;
11
+ [key: string]: string | undefined;
12
+ }
13
+
14
+ interface NestboxConfig {
15
+ projects: ProjectsConfig;
16
+ }
17
+
18
+ // Utility functions for project configuration
19
+ export function getNestboxConfigPath(): string {
20
+ return path.join(process.cwd(), '.nestboxrc');
21
+ }
22
+
23
+ export function readNestboxConfig(): NestboxConfig {
24
+ const configPath = getNestboxConfigPath();
25
+ if (fs.existsSync(configPath)) {
26
+ try {
27
+ const content = fs.readFileSync(configPath, 'utf8');
28
+ return JSON.parse(content);
29
+ } catch (error) {
30
+ return { projects: {} };
31
+ }
32
+ }
33
+ return { projects: {} };
34
+ }
35
+
36
+ export function writeNestboxConfig(config: NestboxConfig): void {
37
+ const configPath = getNestboxConfigPath();
38
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
39
+ }
40
+
41
+ export function registerProjectCommands(program: Command): void {
42
+
43
+ const authToken = getAuthToken();
44
+ const configuration = new Configuration({
45
+ basePath: authToken?.serverUrl,
46
+ baseOptions:{
47
+ headers: {
48
+ "Authorization": authToken?.token,
49
+ }
50
+ }
51
+ });
52
+
53
+ const projectsApi = new ProjectsApi(configuration);
54
+
55
+ // Create the main project command
56
+ const projectCommand = program
57
+ .command('project')
58
+ .description('Manage Nestbox projects');
59
+
60
+ // Add the basic 'use' subcommand
61
+ projectCommand
62
+ .command('use <project-name>')
63
+ .description('Set default project for all commands')
64
+ .action((projectName: string) => {
65
+ try {
66
+ if (!authToken) {
67
+ console.error(chalk.red('No authentication token found. Please log in first.'));
68
+ return;
69
+ }
70
+ const config = readNestboxConfig();
71
+
72
+ // Set the default project
73
+ config.projects = config.projects || {};
74
+ config.projects.default = projectName;
75
+
76
+ projectsApi.projectControllerGetAllProjects()
77
+ .then((response) => {
78
+ // Check if the project exists
79
+ const projectExists = response.data.data.projects.some((project: any) => project.name === projectName);
80
+ if (!projectExists) {
81
+ throw new Error(`Project '${projectName}' does not exist.`);
82
+ }
83
+ // Write the configuration
84
+ writeNestboxConfig(config);
85
+ console.log(chalk.green(`Default project set to '${projectName}'`));
86
+ })
87
+ .catch((error) => {
88
+ console.error(error.message);
89
+ });
90
+
91
+ } catch (error) {
92
+ console.error(chalk.red('Error setting default project:'), error instanceof Error ? error.message : 'Unknown error');
93
+ }
94
+ });
95
+
96
+ // Add basic 'add' subcommand
97
+ projectCommand
98
+ .command('add <project-name> [alias]')
99
+ .description('Add a project with optional alias')
100
+ .action((projectName: string, alias?: string) => {
101
+ try {
102
+ if (!authToken) {
103
+ console.error(chalk.red('No authentication token found. Please log in first.'));
104
+ return;
105
+ }
106
+ const config = readNestboxConfig();
107
+ config.projects = config.projects || {};
108
+
109
+ // Check if the project already exists
110
+ if (config.projects[projectName]) {
111
+ console.error(chalk.red(`Project '${projectName}' already exists.`));
112
+ return;
113
+ }
114
+ // Check if the alias already exists
115
+ if (alias && config.projects[alias]) {
116
+ console.error(chalk.red(`Alias '${alias}' already exists.`));
117
+ return;
118
+ }
119
+
120
+ if (alias) {
121
+ config.projects[alias] = projectName;
122
+ console.log(chalk.green(`Added project '${projectName}' with alias '${alias}'`));
123
+ } else {
124
+ config.projects[projectName] = projectName;
125
+ console.log(chalk.green(`Added project '${projectName}'`));
126
+ }
127
+
128
+ // If this is the first project, set it as default
129
+ if (!config.projects.default) {
130
+ config.projects.default = projectName;
131
+ console.log(chalk.green(`Set '${projectName}' as the default project`));
132
+ }
133
+
134
+ writeNestboxConfig(config);
135
+ } catch (error) {
136
+ console.error(chalk.red('Error adding project:'), error instanceof Error ? error.message : 'Unknown error');
137
+ }
138
+ });
139
+ }
package/src/index.ts CHANGED
@@ -1 +1,24 @@
1
- console.log('Hello World');
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { registerAuthCommands } from './commands/auth';
4
+ import { registerProjectCommands } from './commands/projects';
5
+ import { registerComputeProgram } from './commands/compute';
6
+
7
+ // Setup the CLI program
8
+ const program = new Command();
9
+ program
10
+ .name('nestbox')
11
+ .description('CLI tool for the Nestbox AI platform')
12
+ .version('1.0.0');
13
+
14
+ // Register command groups
15
+ registerAuthCommands(program);
16
+ registerProjectCommands(program);
17
+ registerComputeProgram(program);
18
+
19
+ // Parse command line arguments
20
+ program.parse(process.argv);
21
+ // Only show help if no arguments were provided
22
+ if (process.argv.length === 2) {
23
+ program.help();
24
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * User credentials stored in the config file
3
+ */
4
+ export interface UserCredentials {
5
+ domain: string;
6
+ email: string;
7
+ token: string;
8
+ apiServerUrl: string;
9
+ name: string;
10
+ picture: string;
11
+ timestamp?: number;
12
+ }
@@ -0,0 +1,177 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ import chalk from 'chalk';
5
+ import { UserCredentials } from '../types/auth';
6
+
7
+ // Config path
8
+ const CONFIG_DIR = path.join(os.homedir(), '.config', '.nestbox');
9
+
10
+ // Ensure config directory exists
11
+ if (!fs.existsSync(CONFIG_DIR)) {
12
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
13
+ }
14
+
15
+ /**
16
+ * Get authentication token for a specific domain
17
+ */
18
+ export function getAuthToken(domain?: string): {token: string, serverUrl: string, accessToken?: string} | null {
19
+ try {
20
+ const files = fs.readdirSync(CONFIG_DIR);
21
+
22
+ if (!domain) {
23
+ // If no domain is provided, return the first token found
24
+ const tokenFiles = files.filter(file => file.endsWith('.json'));
25
+ if (tokenFiles.length === 0) {
26
+ return null;
27
+ }
28
+
29
+ const configData = JSON.parse(fs.readFileSync(path.join(CONFIG_DIR, tokenFiles[0])).toString());
30
+ return {
31
+ token: configData.token,
32
+ serverUrl: configData.apiServerUrl,
33
+ accessToken: configData.accessToken,
34
+ };
35
+ }
36
+ const domainFiles = files.filter(file => file.endsWith(`_${domain}.json`));
37
+
38
+ if (domainFiles.length === 0) {
39
+ return null;
40
+ }
41
+
42
+ // If multiple accounts, sort by last used and take the most recent
43
+ let configData: UserCredentials;
44
+
45
+ if (domainFiles.length > 1) {
46
+ const allConfigs = domainFiles.map(file => {
47
+ const data = JSON.parse(fs.readFileSync(path.join(CONFIG_DIR, file)).toString()) as UserCredentials;
48
+ return {
49
+ file,
50
+ data,
51
+ };
52
+ });
53
+
54
+ // Sort by last used, most recent first
55
+ configData = allConfigs[0].data;
56
+ } else {
57
+ // Just one file
58
+ const configFile = domainFiles[0];
59
+ configData = JSON.parse(fs.readFileSync(path.join(CONFIG_DIR, configFile)).toString());
60
+ }
61
+
62
+ return {
63
+ token: configData.token,
64
+ serverUrl: configData.apiServerUrl,
65
+ }
66
+ } catch (error) {
67
+ console.error('Error getting auth token:', error);
68
+ return null;
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Get user credentials for a specific domain and email
74
+ */
75
+ export function getUserCredentials(domain: string, email?: string): UserCredentials | null {
76
+ try {
77
+ const files = fs.readdirSync(CONFIG_DIR);
78
+ let targetFiles: string[];
79
+
80
+ if (email) {
81
+ // Get specific email for domain
82
+ targetFiles = files.filter(file => file === `${email}_${domain}.json`);
83
+ } else {
84
+ // Get all for domain, sort by last used
85
+ targetFiles = files.filter(file => file.endsWith(`_${domain}.json`));
86
+
87
+ if (targetFiles.length > 1) {
88
+ const allConfigs = targetFiles.map(file => {
89
+ const data = JSON.parse(fs.readFileSync(path.join(CONFIG_DIR, file)).toString()) as UserCredentials;
90
+ return {
91
+ file,
92
+ };
93
+ });
94
+
95
+ // Sort by last used, most recent first
96
+ targetFiles = [allConfigs[0].file];
97
+ }
98
+ }
99
+
100
+ if (targetFiles.length === 0) {
101
+ return null;
102
+ }
103
+
104
+ return JSON.parse(fs.readFileSync(path.join(CONFIG_DIR, targetFiles[0])).toString());
105
+ } catch (error) {
106
+ console.error('Error getting user credentials:', error);
107
+ return null;
108
+ }
109
+ }
110
+
111
+ /**
112
+ * List all saved credentials
113
+ */
114
+ export function listCredentials(): Array<{
115
+ domain: string;
116
+ email: string;
117
+ name?: string;
118
+ authMethod?: string;
119
+ lastUsed?: number;
120
+ }> {
121
+ try {
122
+ const files = fs.readdirSync(CONFIG_DIR);
123
+ return files.map(file => {
124
+ try {
125
+ const data = JSON.parse(fs.readFileSync(path.join(CONFIG_DIR, file)).toString()) as UserCredentials;
126
+ return {
127
+ domain: data.domain,
128
+ email: data.email,
129
+ name: data.name,
130
+ };
131
+ } catch (error) {
132
+ // Skip invalid files
133
+ return null;
134
+ }
135
+ }).filter(Boolean) as Array<{
136
+ domain: string;
137
+ email: string;
138
+ name?: string;
139
+ authMethod?: string;
140
+ lastUsed?: number;
141
+ }>;
142
+ } catch (error) {
143
+ console.error('Error listing credentials:', error);
144
+ return [];
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Remove credentials for a domain/email combination
150
+ */
151
+ export function removeCredentials(domain: string, email?: string): boolean {
152
+ try {
153
+ const files = fs.readdirSync(CONFIG_DIR);
154
+ let domainFiles: string[];
155
+
156
+ if (email) {
157
+ // Remove specific email for domain
158
+ domainFiles = files.filter(file => file === `${email}_${domain}.json`);
159
+ } else {
160
+ // Remove all for domain
161
+ domainFiles = files.filter(file => file.endsWith(`_${domain}.json`));
162
+ }
163
+
164
+ if (domainFiles.length === 0) {
165
+ return false;
166
+ }
167
+
168
+ domainFiles.forEach(file => {
169
+ fs.unlinkSync(path.join(CONFIG_DIR, file));
170
+ });
171
+
172
+ return true;
173
+ } catch (error) {
174
+ console.error('Error removing credentials:', error);
175
+ return false;
176
+ }
177
+ }
package/tsconfig.json CHANGED
@@ -108,6 +108,9 @@
108
108
 
109
109
  /* Completeness */
110
110
  // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
111
- "skipLibCheck": true /* Skip type checking all .d.ts files. */
111
+ "skipLibCheck": true , /* Skip type checking all .d.ts files. */
112
+ "declaration": true,
113
+ "sourceMap": true,
114
+ "resolveJsonModule": true
112
115
  }
113
116
  }