@nestbox-ai/cli 1.0.48 → 1.0.49
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/package.json +7 -1
- package/.github/workflows/generate-client.yml +0 -43
- package/.github/workflows/test.yml +0 -54
- package/.nestboxrc +0 -5
- package/src/commands/agent/apiUtils.ts +0 -113
- package/src/commands/agent/create.ts +0 -271
- package/src/commands/agent/deploy.ts +0 -523
- package/src/commands/agent/index.ts +0 -8
- package/src/commands/agent/list.ts +0 -104
- package/src/commands/agent/remove.ts +0 -103
- package/src/commands/agent/yaml-schema.ts +0 -57
- package/src/commands/agent.ts +0 -21
- package/src/commands/auth/index.ts +0 -3
- package/src/commands/auth/login.ts +0 -184
- package/src/commands/auth/logout.ts +0 -110
- package/src/commands/auth.ts +0 -12
- package/src/commands/compute/apiUtils.ts +0 -28
- package/src/commands/compute/create.ts +0 -195
- package/src/commands/compute/delete.ts +0 -147
- package/src/commands/compute/index.ts +0 -7
- package/src/commands/compute/list.ts +0 -117
- package/src/commands/compute.ts +0 -19
- package/src/commands/document/apiUtils.ts +0 -22
- package/src/commands/document/collectionCreate.ts +0 -46
- package/src/commands/document/collectionDelete.ts +0 -45
- package/src/commands/document/collectionGet.ts +0 -47
- package/src/commands/document/collectionList.ts +0 -52
- package/src/commands/document/collectionUpdate.ts +0 -53
- package/src/commands/document/docAdd.ts +0 -56
- package/src/commands/document/docDelete.ts +0 -47
- package/src/commands/document/docGet.ts +0 -49
- package/src/commands/document/docSearch.ts +0 -70
- package/src/commands/document/docUpdate.ts +0 -56
- package/src/commands/document/docUploadFile.ts +0 -55
- package/src/commands/document/index.ts +0 -17
- package/src/commands/document.ts +0 -48
- package/src/commands/generate/project.ts +0 -199
- package/src/commands/generate.ts +0 -15
- package/src/commands/image/apiUtils.ts +0 -22
- package/src/commands/image/display.ts +0 -34
- package/src/commands/image/index.ts +0 -4
- package/src/commands/image/list.ts +0 -61
- package/src/commands/image.ts +0 -15
- package/src/commands/project/add.ts +0 -47
- package/src/commands/project/apiUtils.ts +0 -20
- package/src/commands/project/index.ts +0 -5
- package/src/commands/project/list.ts +0 -78
- package/src/commands/project/use.ts +0 -45
- package/src/commands/project.ts +0 -19
- package/src/index.ts +0 -39
- package/src/types/agentType.ts +0 -7
- package/src/types/agentYaml.ts +0 -107
- package/src/types/auth.ts +0 -12
- package/src/types/statusMapping.ts +0 -8
- package/src/utils/agent.ts +0 -170
- package/src/utils/api.ts +0 -64
- package/src/utils/auth.ts +0 -140
- package/src/utils/config.ts +0 -37
- package/src/utils/error.ts +0 -168
- package/src/utils/plopGenerator.ts +0 -133
- package/src/utils/project.ts +0 -88
- package/src/utils/user.ts +0 -28
- package/src/utils/validation.ts +0 -22
- package/templates/base-js/index.js.hbs +0 -30
- package/templates/base-js/nestbox-agents.yaml.hbs +0 -12
- package/templates/base-js/package.json +0 -15
- package/templates/base-py/main.py.hbs +0 -42
- package/templates/base-py/nestbox-agents.yaml.hbs +0 -12
- package/templates/base-py/pyproject.toml +0 -19
- package/templates/base-ts/eslint.config.mjs +0 -27
- package/templates/base-ts/nestbox-agents.yaml.hbs +0 -12
- package/templates/base-ts/nestbox.config.json +0 -9
- package/templates/base-ts/package.json +0 -24
- package/templates/base-ts/src/index.ts.hbs +0 -29
- package/templates/base-ts/tsconfig.json +0 -14
- package/templates/chatbot-js/index.js.hbs +0 -18
- package/templates/chatbot-js/package-lock.json +0 -1571
- package/templates/chatbot-js/package.json +0 -15
- package/templates/chatbot-py/main.py.hbs +0 -42
- package/templates/chatbot-py/nestbox-agents.yaml.hbs +0 -12
- package/templates/chatbot-py/pyproject.toml +0 -19
- package/templates/chatbot-ts/eslint.config.mjs +0 -27
- package/templates/chatbot-ts/package.json +0 -24
- package/templates/chatbot-ts/src/index.ts.hbs +0 -18
- package/templates/chatbot-ts/tsconfig.json +0 -14
- package/test/README.md +0 -52
- package/test/agent.test.ts +0 -154
- package/test/auth.test.ts +0 -71
- package/test/compute.test.ts +0 -135
- package/test/document.test.ts +0 -217
- package/test/generate.test.ts +0 -67
- package/test/image.test.ts +0 -107
- package/test/mocks.ts +0 -122
- package/test/project.test.ts +0 -108
- package/test/setup.ts +0 -121
- package/tsconfig.json +0 -118
- package/vitest.config.d.ts +0 -2
- package/vitest.config.js +0 -23
- package/vitest.config.js.map +0 -1
- package/vitest.config.ts +0 -21
package/src/types/agentYaml.ts
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Type definition for Agent YAML configuration file
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Represents a parameter for an agent in the YAML configuration
|
|
7
|
-
*/
|
|
8
|
-
export interface AgentParameter {
|
|
9
|
-
/**
|
|
10
|
-
* Name of the parameter
|
|
11
|
-
*/
|
|
12
|
-
name: string;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Description of the parameter
|
|
16
|
-
*/
|
|
17
|
-
description: string;
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Default value for the parameter
|
|
21
|
-
*/
|
|
22
|
-
default?: any;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Default value as a string (used internally)
|
|
26
|
-
*/
|
|
27
|
-
default_value?: string;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Whether this parameter is configurable by users
|
|
31
|
-
* @default true
|
|
32
|
-
*/
|
|
33
|
-
isUserParam?: boolean;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Represents a single agent definition in the YAML configuration
|
|
38
|
-
*/
|
|
39
|
-
export interface AgentDefinition {
|
|
40
|
-
/**
|
|
41
|
-
* Name of the agent (required)
|
|
42
|
-
*/
|
|
43
|
-
name: string;
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Description of the agent's purpose
|
|
47
|
-
* @default `AI agent for ${name}`
|
|
48
|
-
*/
|
|
49
|
-
goal?: string;
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Agent type ("CHAT" or "AGENT")
|
|
53
|
-
* CHAT creates a chatbot, AGENT creates a regular agent
|
|
54
|
-
* @default "CHAT"
|
|
55
|
-
*/
|
|
56
|
-
type?: string;
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Machine manifest ID for the agent
|
|
60
|
-
* @default "llamaindex-agent"
|
|
61
|
-
*/
|
|
62
|
-
machineManifestId?: string;
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Instance IP address
|
|
66
|
-
* @default "http://34.121.124.21"
|
|
67
|
-
*/
|
|
68
|
-
instanceIP?: string;
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Name of the machine
|
|
72
|
-
* @default "agent-instance"
|
|
73
|
-
*/
|
|
74
|
-
machineName?: string;
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Machine instance ID
|
|
78
|
-
* @default 17
|
|
79
|
-
*/
|
|
80
|
-
machineInstanceId?: number;
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Name of the instance
|
|
84
|
-
*/
|
|
85
|
-
instanceName?: string;
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Model base ID
|
|
89
|
-
* @default ""
|
|
90
|
-
*/
|
|
91
|
-
modelBaseId?: string;
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Agent parameters
|
|
95
|
-
*/
|
|
96
|
-
parameters?: AgentParameter[];
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Overall structure of the YAML configuration file
|
|
101
|
-
*/
|
|
102
|
-
export interface AgentYamlConfig {
|
|
103
|
-
/**
|
|
104
|
-
* Array of agent definitions
|
|
105
|
-
*/
|
|
106
|
-
agents: AgentDefinition[];
|
|
107
|
-
}
|
package/src/types/auth.ts
DELETED
package/src/utils/agent.ts
DELETED
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import fs from "fs";
|
|
3
|
-
import chalk from "chalk";
|
|
4
|
-
import ora from "ora";
|
|
5
|
-
import { promisify } from "util";
|
|
6
|
-
import { exec } from "child_process";
|
|
7
|
-
import AdmZip from "adm-zip";
|
|
8
|
-
import * as os from "os";
|
|
9
|
-
import axios from "axios";
|
|
10
|
-
|
|
11
|
-
const execAsync = promisify(exec);
|
|
12
|
-
|
|
13
|
-
export async function findProjectRoot(startDir = process.cwd()) {
|
|
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
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Function to load and parse nestbox.config.json if it exists
|
|
34
|
-
export function loadNestboxConfig(projectRoot: any) {
|
|
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;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Function to detect if a directory contains TypeScript files
|
|
54
|
-
export function isTypeScriptProject(directoryPath: any) {
|
|
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
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export async function runPredeployScripts(scripts: any, projectRoot: any) {
|
|
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
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
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;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
export interface TemplateInfo {
|
|
148
|
-
name: string;
|
|
149
|
-
description: string;
|
|
150
|
-
fileId: string;
|
|
151
|
-
lang: string;
|
|
152
|
-
type: string;
|
|
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/src/utils/api.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
|
-
import { Configuration } from "@nestbox-ai/admin";
|
|
3
|
-
import { getAuthToken } from "./auth";
|
|
4
|
-
|
|
5
|
-
export interface AuthResult {
|
|
6
|
-
authToken: {
|
|
7
|
-
token: string;
|
|
8
|
-
serverUrl: string;
|
|
9
|
-
accessToken?: string;
|
|
10
|
-
};
|
|
11
|
-
configuration: Configuration;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Common authentication and configuration setup
|
|
16
|
-
* Returns authentication token and configured API client
|
|
17
|
-
* Exits the process if authentication fails
|
|
18
|
-
*/
|
|
19
|
-
export function setupAuthAndConfig(): AuthResult | null {
|
|
20
|
-
const authToken = getAuthToken();
|
|
21
|
-
|
|
22
|
-
if (!authToken) {
|
|
23
|
-
console.error(chalk.red('No authentication token found. Please login first.'));
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const configuration = new Configuration({
|
|
28
|
-
basePath: authToken.serverUrl,
|
|
29
|
-
baseOptions: {
|
|
30
|
-
headers: {
|
|
31
|
-
"Authorization": authToken.token,
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
return {
|
|
37
|
-
authToken,
|
|
38
|
-
configuration
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Wrapper function that ensures authentication is set up before executing a callback
|
|
44
|
-
* Automatically handles authentication errors and provides configured API instances
|
|
45
|
-
*/
|
|
46
|
-
export async function withAuth<T>(
|
|
47
|
-
callback: (authResult: AuthResult) => Promise<T>
|
|
48
|
-
): Promise<T | void> {
|
|
49
|
-
const authResult = setupAuthAndConfig();
|
|
50
|
-
|
|
51
|
-
if (!authResult) {
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
try {
|
|
56
|
-
return await callback(authResult);
|
|
57
|
-
} catch (error: any) {
|
|
58
|
-
if (error.response && error.response.status === 401) {
|
|
59
|
-
console.error(chalk.red('Authentication token has expired. Please login again using "nestbox login <domain>".'));
|
|
60
|
-
} else {
|
|
61
|
-
throw error; // Re-throw non-auth errors
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
package/src/utils/auth.ts
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
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
|
-
accessToken: configData.accessToken,
|
|
66
|
-
}
|
|
67
|
-
} catch (error) {
|
|
68
|
-
console.error('Error getting auth token:', error);
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* List all saved credentials
|
|
75
|
-
*/
|
|
76
|
-
export function listCredentials(): Array<{
|
|
77
|
-
domain: string;
|
|
78
|
-
email: string;
|
|
79
|
-
name?: string;
|
|
80
|
-
authMethod?: string;
|
|
81
|
-
lastUsed?: number;
|
|
82
|
-
}> {
|
|
83
|
-
try {
|
|
84
|
-
const files = fs.readdirSync(CONFIG_DIR);
|
|
85
|
-
return files.map(file => {
|
|
86
|
-
try {
|
|
87
|
-
const data = JSON.parse(fs.readFileSync(path.join(CONFIG_DIR, file)).toString()) as UserCredentials;
|
|
88
|
-
return {
|
|
89
|
-
domain: data.domain,
|
|
90
|
-
email: data.email,
|
|
91
|
-
name: data.name,
|
|
92
|
-
};
|
|
93
|
-
} catch (error) {
|
|
94
|
-
// Skip invalid files
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
}).filter(Boolean) as Array<{
|
|
98
|
-
domain: string;
|
|
99
|
-
email: string;
|
|
100
|
-
name?: string;
|
|
101
|
-
authMethod?: string;
|
|
102
|
-
lastUsed?: number;
|
|
103
|
-
}>;
|
|
104
|
-
} catch (error) {
|
|
105
|
-
console.error('Error listing credentials:', error);
|
|
106
|
-
return [];
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Remove credentials for a domain/email combination
|
|
112
|
-
*/
|
|
113
|
-
export function removeCredentials(domain: string, email?: string): boolean {
|
|
114
|
-
try {
|
|
115
|
-
const files = fs.readdirSync(CONFIG_DIR);
|
|
116
|
-
let domainFiles: string[];
|
|
117
|
-
|
|
118
|
-
if (email) {
|
|
119
|
-
// Remove specific email for domain
|
|
120
|
-
const emailFile = email.replace('@', '_at_');
|
|
121
|
-
domainFiles = files.filter(file => file === `${emailFile}_${domain}.json`);
|
|
122
|
-
} else {
|
|
123
|
-
// Remove all for domain
|
|
124
|
-
domainFiles = files.filter(file => file.endsWith(`_${domain}.json`));
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (domainFiles.length === 0) {
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
domainFiles.forEach(file => {
|
|
132
|
-
fs.unlinkSync(path.join(CONFIG_DIR, file));
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
return true;
|
|
136
|
-
} catch (error) {
|
|
137
|
-
console.error('Error removing credentials:', error);
|
|
138
|
-
return false;
|
|
139
|
-
}
|
|
140
|
-
}
|
package/src/utils/config.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
|
|
4
|
-
// Define a type for the projects configuration
|
|
5
|
-
interface ProjectsConfig {
|
|
6
|
-
default?: string;
|
|
7
|
-
[key: string]: string | undefined;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
interface NestboxConfig {
|
|
11
|
-
projects: ProjectsConfig;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// Utility functions for project configuration
|
|
15
|
-
export function getNestboxConfigPath(): string {
|
|
16
|
-
return path.join(process.cwd(), '.nestboxrc');
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function readNestboxConfig(): NestboxConfig {
|
|
20
|
-
const configPath = getNestboxConfigPath();
|
|
21
|
-
if (fs.existsSync(configPath)) {
|
|
22
|
-
try {
|
|
23
|
-
const content = fs.readFileSync(configPath, 'utf8');
|
|
24
|
-
return JSON.parse(content);
|
|
25
|
-
} catch (error) {
|
|
26
|
-
return { projects: {} };
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return { projects: {} };
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function writeNestboxConfig(config: NestboxConfig): void {
|
|
33
|
-
const configPath = getNestboxConfigPath();
|
|
34
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export type { ProjectsConfig, NestboxConfig };
|
package/src/utils/error.ts
DELETED
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
// utils/error.ts
|
|
2
|
-
import { AuthApi, Configuration, OAuthLoginRequestDTOTypeEnum } from '@nestbox-ai/admin';
|
|
3
|
-
import { getAuthToken } from './auth';
|
|
4
|
-
import fs from 'fs';
|
|
5
|
-
import path from 'path';
|
|
6
|
-
import os from 'os';
|
|
7
|
-
import chalk from 'chalk';
|
|
8
|
-
|
|
9
|
-
interface TokenRefreshResult {
|
|
10
|
-
success: boolean;
|
|
11
|
-
newToken?: string;
|
|
12
|
-
error?: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Attempts to refresh the authentication token using stored credentials
|
|
17
|
-
*/
|
|
18
|
-
async function refreshAuthToken(serverUrl: string, accessToken: string): Promise<TokenRefreshResult> {
|
|
19
|
-
try {
|
|
20
|
-
// Get the stored credentials to extract user info
|
|
21
|
-
const configDir = path.join(os.homedir(), '.config', '.nestbox');
|
|
22
|
-
const files = fs.readdirSync(configDir);
|
|
23
|
-
|
|
24
|
-
// Find the credential file that matches this server URL
|
|
25
|
-
let userCredentials: any = null;
|
|
26
|
-
for (const file of files) {
|
|
27
|
-
try {
|
|
28
|
-
const data = JSON.parse(fs.readFileSync(path.join(configDir, file), 'utf8'));
|
|
29
|
-
if (data.apiServerUrl === serverUrl && data.accessToken === accessToken) {
|
|
30
|
-
userCredentials = data;
|
|
31
|
-
break;
|
|
32
|
-
}
|
|
33
|
-
} catch (e) {
|
|
34
|
-
// Skip invalid files
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (!userCredentials) {
|
|
39
|
-
return { success: false, error: 'Could not find stored credentials' };
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Create new configuration with the access token
|
|
43
|
-
const configuration = new Configuration({
|
|
44
|
-
basePath: serverUrl,
|
|
45
|
-
accessToken: accessToken,
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
const authApi = new AuthApi(configuration);
|
|
49
|
-
|
|
50
|
-
// Try to re-authenticate using the stored Google OAuth token
|
|
51
|
-
const response = await authApi.authControllerOAuthLogin({
|
|
52
|
-
providerId: accessToken,
|
|
53
|
-
type: OAuthLoginRequestDTOTypeEnum.Google,
|
|
54
|
-
email: userCredentials.email,
|
|
55
|
-
profilePictureUrl: userCredentials.picture || '',
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
const newToken = response.data.token;
|
|
59
|
-
|
|
60
|
-
// Update the stored credentials with the new token
|
|
61
|
-
const fileName = `${userCredentials.email.replace('@', '_at_')}_${userCredentials.domain}.json`;
|
|
62
|
-
const filePath = path.join(configDir, fileName);
|
|
63
|
-
|
|
64
|
-
userCredentials.token = newToken;
|
|
65
|
-
userCredentials.timestamp = new Date().toISOString();
|
|
66
|
-
|
|
67
|
-
fs.writeFileSync(filePath, JSON.stringify(userCredentials, null, 2));
|
|
68
|
-
|
|
69
|
-
return { success: true, newToken };
|
|
70
|
-
} catch (error: any) {
|
|
71
|
-
console.error(chalk.yellow('Token refresh failed:'), error.message);
|
|
72
|
-
return { success: false, error: error.message };
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Enhanced 401 error handler with automatic token refresh
|
|
78
|
-
*/
|
|
79
|
-
export async function handle401Error(error: any, retryCallback?: () => Promise<any>): Promise<any> {
|
|
80
|
-
if (error.response && error.response.status === 401) {
|
|
81
|
-
// Get current auth token info
|
|
82
|
-
const authInfo = getAuthToken();
|
|
83
|
-
|
|
84
|
-
if (!authInfo || !authInfo.accessToken) {
|
|
85
|
-
throw new Error('Authentication token has expired. Please login again using "nestbox login <domain>".');
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
console.log(chalk.yellow('Authentication token expired. Attempting to refresh...'));
|
|
89
|
-
|
|
90
|
-
// Try to refresh the token
|
|
91
|
-
const refreshResult = await refreshAuthToken(authInfo.serverUrl, authInfo.accessToken);
|
|
92
|
-
|
|
93
|
-
if (refreshResult.success && retryCallback) {
|
|
94
|
-
console.log(chalk.green('Token refreshed successfully. Retrying request...'));
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
// Retry the original request with the new token
|
|
98
|
-
const result = await retryCallback();
|
|
99
|
-
return { success: true, data: result };
|
|
100
|
-
} catch (retryError: any) {
|
|
101
|
-
// If retry also fails with 401, the refresh didn't work properly
|
|
102
|
-
if (retryError.response && retryError.response.status === 401) {
|
|
103
|
-
throw new Error('Authentication failed after token refresh. Please login again using "nestbox login <domain>".');
|
|
104
|
-
}
|
|
105
|
-
// Re-throw other errors
|
|
106
|
-
throw retryError;
|
|
107
|
-
}
|
|
108
|
-
} else {
|
|
109
|
-
// Refresh failed
|
|
110
|
-
throw new Error('Authentication token has expired and automatic refresh failed. Please login again using "nestbox login <domain>".');
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return null;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Wrapper function to make API calls with automatic retry on 401
|
|
119
|
-
*/
|
|
120
|
-
export async function withTokenRefresh<T>(
|
|
121
|
-
apiCall: () => Promise<T>,
|
|
122
|
-
onRetry?: () => void
|
|
123
|
-
): Promise<T> {
|
|
124
|
-
try {
|
|
125
|
-
return await apiCall();
|
|
126
|
-
} catch (error: any) {
|
|
127
|
-
if (error.response && error.response.status === 401) {
|
|
128
|
-
// Get current auth token info
|
|
129
|
-
const authInfo = getAuthToken();
|
|
130
|
-
|
|
131
|
-
if (!authInfo || !authInfo.accessToken) {
|
|
132
|
-
throw new Error('Authentication token has expired. Please login again using "nestbox login <domain>".');
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
console.log(chalk.yellow('Authentication token expired. Attempting to refresh...'));
|
|
136
|
-
|
|
137
|
-
// Try to refresh the token
|
|
138
|
-
const refreshResult = await refreshAuthToken(authInfo.serverUrl, authInfo.accessToken);
|
|
139
|
-
|
|
140
|
-
if (refreshResult.success) {
|
|
141
|
-
console.log(chalk.green('Token refreshed successfully. Retrying request...'));
|
|
142
|
-
|
|
143
|
-
// If onRetry callback is provided, call it to reinitialize API clients
|
|
144
|
-
if (onRetry) {
|
|
145
|
-
onRetry();
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
try {
|
|
149
|
-
// Retry the original API call
|
|
150
|
-
return await apiCall();
|
|
151
|
-
} catch (retryError: any) {
|
|
152
|
-
// If retry also fails with 401, the refresh didn't work properly
|
|
153
|
-
if (retryError.response && retryError.response.status === 401) {
|
|
154
|
-
throw new Error('Authentication failed after token refresh. Please login again using "nestbox login <domain>".');
|
|
155
|
-
}
|
|
156
|
-
// Re-throw other errors
|
|
157
|
-
throw retryError;
|
|
158
|
-
}
|
|
159
|
-
} else {
|
|
160
|
-
// Refresh failed
|
|
161
|
-
throw new Error('Authentication token has expired and automatic refresh failed. Please login again using "nestbox login <domain>".');
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// If not a 401 error, re-throw
|
|
166
|
-
throw error;
|
|
167
|
-
}
|
|
168
|
-
}
|