fnos-cli 0.1.0 → 0.2.0
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/README.md +53 -1
- package/package.json +1 -3
- package/src/commands/auth.js +4 -3
- package/src/commands/index.js +22 -1
- package/src/index.js +1 -1
- package/src/utils/auth-helper.js +68 -0
- package/src/utils/client.js +49 -27
- package/src/utils/settings.js +17 -0
package/README.md
CHANGED
|
@@ -59,7 +59,27 @@ fnos login -e nas-9.timandes.net:5666 -u SystemMonitor -p yourpassword
|
|
|
59
59
|
|
|
60
60
|
登录成功后,凭证会保存在 `~/.fnos/settings.json` 文件中,后续命令无需重复输入。
|
|
61
61
|
|
|
62
|
-
### 2.
|
|
62
|
+
### 2. 使用命令行凭证参数
|
|
63
|
+
|
|
64
|
+
除了使用 `login` 保存凭证外,您还可以直接在命令中提供连接参数:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
fnos <command> -e <endpoint> -u <username> -p <password>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
例如:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
fnos resmon.cpu -e nas-9.timandes.net:5666 -u SystemMonitor -p yourpassword
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**注意事项**:
|
|
77
|
+
- 三个参数(`-e`、`-u`、`-p`)必须同时提供,不能只提供部分参数
|
|
78
|
+
- 使用命令行参数提供的凭证**不会**保存到配置文件中,适合临时使用
|
|
79
|
+
- 如果未提供这三个参数,会自动使用已保存的凭证(需要先执行 `login`)
|
|
80
|
+
- 这种方式适合自动化脚本或临时连接到不同服务器
|
|
81
|
+
|
|
82
|
+
### 3. 使用命令
|
|
63
83
|
|
|
64
84
|
登录后即可执行各种命令:
|
|
65
85
|
|
|
@@ -120,6 +140,38 @@ fnos resmon.cpu -v > output.json 2>log.txt
|
|
|
120
140
|
fnos resmon.cpu 2>/dev/null
|
|
121
141
|
```
|
|
122
142
|
|
|
143
|
+
### 认证参数
|
|
144
|
+
|
|
145
|
+
所有命令(`login` 和 `logout` 除外)都支持以下可选的认证参数:
|
|
146
|
+
|
|
147
|
+
| 参数 | 说明 |
|
|
148
|
+
|------|------|
|
|
149
|
+
| `-e, --endpoint <endpoint>` | 服务器端点(例如:nas-9.timandes.net:5666) |
|
|
150
|
+
| `-u, --username <username>` | 用户名 |
|
|
151
|
+
| `-p, --password <password>` | 密码 |
|
|
152
|
+
|
|
153
|
+
**使用规则**:
|
|
154
|
+
|
|
155
|
+
1. **三个参数必须同时提供**:如果提供任何一个参数,必须同时提供另外两个参数
|
|
156
|
+
2. **临时使用不保存**:通过命令行参数提供的凭证不会被保存到配置文件
|
|
157
|
+
3. **优先使用命令行参数**:如果同时提供了命令行参数和配置文件中有保存的凭证,优先使用命令行参数
|
|
158
|
+
4. **回退到配置文件**:如果未提供这三个参数,会自动使用 `login` 保存的凭证
|
|
159
|
+
|
|
160
|
+
**示例**:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
# 使用命令行参数(临时使用,不保存)
|
|
164
|
+
fnos resmon.cpu -e nas-9.timandes.net:5666 -u SystemMonitor -p yourpassword
|
|
165
|
+
|
|
166
|
+
# 错误示例:只提供部分参数
|
|
167
|
+
fnos resmon.cpu -e nas-9.timandes.net:5666
|
|
168
|
+
# Error: When using -e/--endpoint, you must also provide -u/--username and -p/--password.
|
|
169
|
+
|
|
170
|
+
# 使用已保存的凭证(需要先执行 login)
|
|
171
|
+
fnos login -e nas-9.timandes.net:5666 -u SystemMonitor -p yourpassword
|
|
172
|
+
fnos resmon.cpu # 不需要再提供参数
|
|
173
|
+
```
|
|
174
|
+
|
|
123
175
|
### 资源监控命令
|
|
124
176
|
|
|
125
177
|
| 命令 | 说明 |
|
package/package.json
CHANGED
|
@@ -1,3 +1 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "fnos-cli",
|
|
3
|
-
"version": "0.1.0", "description": "CLI client for 飞牛 fnOS system", "main": "src/index.js", "bin": {"fnos": "./bin/fnos"}, "scripts": {"start": "node src/index.js", "test": "mocha test/**/*.test.js"}, "keywords": ["fnos", "cli", "nas"], "author": "", "license": "Apache-2.0", "dependencies": {"commander": "^11.1.0", "fnos": "^0.2.0", "winston": "^3.19.0"}, "devDependencies": {"chai": "^5.3.3", "mocha": "^10.8.2"}}
|
|
1
|
+
{"name": "fnos-cli", "version": "0.2.0", "description": "CLI client for 飞牛 fnOS system", "main": "src/index.js", "bin": {"fnos": "./bin/fnos"}, "scripts": {"start": "node src/index.js", "test": "mocha test/**/*.test.js"}, "keywords": ["fnos", "cli", "nas"], "author": "", "license": "Apache-2.0", "dependencies": {"commander": "^11.1.0", "fnos": "^0.2.0", "winston": "^3.19.0"}, "devDependencies": {"chai": "^5.3.3", "mocha": "^10.8.2"}}
|
package/src/commands/auth.js
CHANGED
|
@@ -23,7 +23,7 @@ function registerLoginCommand(program) {
|
|
|
23
23
|
const { endpoint, username, password } = options;
|
|
24
24
|
|
|
25
25
|
logger.info('Logging in...');
|
|
26
|
-
const client = await createClient({ endpoint, username, password, timeout: 60 });
|
|
26
|
+
const client = await createClient({ endpoint, username, password, timeout: 60, saveCredentials: true });
|
|
27
27
|
logger.info('Login successful! Credentials saved.');
|
|
28
28
|
client.close();
|
|
29
29
|
process.exit(0);
|
|
@@ -44,8 +44,9 @@ function registerLogoutCommand(program) {
|
|
|
44
44
|
.description('Logout and clear saved credentials')
|
|
45
45
|
.action(() => {
|
|
46
46
|
try {
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
const credentials = settings.getCredentials();
|
|
48
|
+
if (credentials && (credentials.endpoint || credentials.username)) {
|
|
49
|
+
settings.clearCredentials();
|
|
49
50
|
console.log('Logged out successfully. Credentials cleared.');
|
|
50
51
|
} else {
|
|
51
52
|
console.log('No saved credentials found.');
|
package/src/commands/index.js
CHANGED
|
@@ -6,6 +6,7 @@ const { COMMAND_MAPPING } = require('../constants');
|
|
|
6
6
|
const { createClient, getSDKInstance } = require('../utils/client');
|
|
7
7
|
const { formatOutput } = require('../utils/formatter');
|
|
8
8
|
const { logger } = require('../utils/logger');
|
|
9
|
+
const { resolveCredentials } = require('../utils/auth-helper');
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Register all dynamic commands based on COMMAND_MAPPING
|
|
@@ -21,6 +22,11 @@ function registerCommands(program) {
|
|
|
21
22
|
const cmd = program.command(fullCommandName)
|
|
22
23
|
.description(commandConfig.description);
|
|
23
24
|
|
|
25
|
+
// Add authentication parameters
|
|
26
|
+
cmd.option('-e, --endpoint <endpoint>', 'Server endpoint (e.g., nas-9.timandes.net:5666)');
|
|
27
|
+
cmd.option('-u, --username <username>', 'Username');
|
|
28
|
+
cmd.option('-p, --password <password>', 'Password');
|
|
29
|
+
|
|
24
30
|
// Add parameters
|
|
25
31
|
if (commandConfig.params) {
|
|
26
32
|
commandConfig.params.forEach(param => {
|
|
@@ -51,8 +57,23 @@ function registerCommands(program) {
|
|
|
51
57
|
}
|
|
52
58
|
}
|
|
53
59
|
|
|
54
|
-
//
|
|
60
|
+
// Resolve credentials (command line or settings file)
|
|
61
|
+
let credentials;
|
|
62
|
+
try {
|
|
63
|
+
credentials = resolveCredentials(options, program);
|
|
64
|
+
} catch (error) {
|
|
65
|
+
if (error.message === 'PARTIAL_CREDENTIALS') {
|
|
66
|
+
// Display friendly error message
|
|
67
|
+
console.error('Error: When using -e/--endpoint, you must also provide -u/--username and -p/--password.');
|
|
68
|
+
console.error('Usage: fnos %s -e <endpoint> -u <username> -p <password>', fullCommandName);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Create client with resolved credentials
|
|
55
75
|
const client = await createClient({
|
|
76
|
+
credentials,
|
|
56
77
|
timeout: 60
|
|
57
78
|
});
|
|
58
79
|
|
package/src/index.js
CHANGED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication helper for credential validation and resolution
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const settings = require('./settings');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Check if partial credentials are provided
|
|
9
|
+
* @param {Object} options - Command line options
|
|
10
|
+
* @returns {boolean} - True if at least one but not all three parameters are provided
|
|
11
|
+
*/
|
|
12
|
+
function hasPartialCredentials(options) {
|
|
13
|
+
const { endpoint, username, password } = options;
|
|
14
|
+
const provided = [endpoint, username, password].filter(Boolean);
|
|
15
|
+
return provided.length > 0 && provided.length < 3;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Validate command line credentials completeness
|
|
20
|
+
* @param {Object} options - Command line options
|
|
21
|
+
* @returns {Object} - { isValid: boolean, hasCredentials: boolean }
|
|
22
|
+
* @throws {Error} - If partial credentials are provided
|
|
23
|
+
*/
|
|
24
|
+
function validateCommandLineCredentials(options) {
|
|
25
|
+
if (hasPartialCredentials(options)) {
|
|
26
|
+
throw new Error('PARTIAL_CREDENTIALS');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const hasCredentials = options.endpoint && options.username && options.password;
|
|
30
|
+
return { isValid: true, hasCredentials };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Resolve and return the final credentials to use
|
|
35
|
+
* @param {Object} options - Command line options
|
|
36
|
+
* @returns {Object} - Credentials object
|
|
37
|
+
* @throws {Error} - If no valid credentials are found or partial credentials are provided
|
|
38
|
+
*/
|
|
39
|
+
function resolveCredentials(options) {
|
|
40
|
+
const validation = validateCommandLineCredentials(options);
|
|
41
|
+
|
|
42
|
+
if (validation.hasCredentials) {
|
|
43
|
+
// Use command line credentials (do NOT save to settings)
|
|
44
|
+
return {
|
|
45
|
+
endpoint: options.endpoint,
|
|
46
|
+
username: options.username,
|
|
47
|
+
password: options.password,
|
|
48
|
+
source: 'command_line'
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Read from settings file
|
|
53
|
+
const credentials = settings.getCredentials();
|
|
54
|
+
if (!credentials || !credentials.endpoint || !credentials.username || !credentials.password) {
|
|
55
|
+
throw new Error('No valid credentials found. Please use "fnos login" or provide -e/-u/-p options.');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
...credentials,
|
|
60
|
+
source: 'settings_file'
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = {
|
|
65
|
+
hasPartialCredentials,
|
|
66
|
+
validateCommandLineCredentials,
|
|
67
|
+
resolveCredentials
|
|
68
|
+
};
|
package/src/utils/client.js
CHANGED
|
@@ -9,16 +9,23 @@ const { logger } = require('./logger');
|
|
|
9
9
|
/**
|
|
10
10
|
* Create authenticated FnosClient
|
|
11
11
|
* @param {Object} options - Options object
|
|
12
|
-
* @param {
|
|
13
|
-
* @param {string} options.
|
|
14
|
-
* @param {string} options.
|
|
15
|
-
* @param {
|
|
12
|
+
* @param {Object} [options.credentials] - Direct credentials object (endpoint, username, password, etc.)
|
|
13
|
+
* @param {string} [options.endpoint] - Server endpoint (legacy)
|
|
14
|
+
* @param {string} [options.username] - Username (legacy)
|
|
15
|
+
* @param {string} [options.password] - Password (legacy)
|
|
16
|
+
* @param {number} [options.timeout] - Connection timeout in seconds
|
|
17
|
+
* @param {boolean} [options.saveCredentials] - Whether to save credentials to settings (default: false)
|
|
16
18
|
* @returns {Promise<FnosClient>} Authenticated client
|
|
17
19
|
*/
|
|
18
20
|
async function createClient(options = {}) {
|
|
19
|
-
const { endpoint, username, password, timeout = 60 } = options;
|
|
21
|
+
const { credentials, endpoint, username, password, timeout = 60, saveCredentials = false } = options;
|
|
20
22
|
|
|
21
|
-
//
|
|
23
|
+
// Use provided credentials object
|
|
24
|
+
if (credentials) {
|
|
25
|
+
return await createClientWithCredentials(credentials, timeout, saveCredentials);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Legacy support: build credentials from individual parameters
|
|
22
29
|
const savedCredentials = settings.getCredentials();
|
|
23
30
|
const finalEndpoint = endpoint || savedCredentials?.endpoint;
|
|
24
31
|
const finalUsername = username || savedCredentials?.username;
|
|
@@ -28,41 +35,56 @@ async function createClient(options = {}) {
|
|
|
28
35
|
throw new Error('Missing credentials. Please run "fnos login" first or provide -e, -u, -p parameters.');
|
|
29
36
|
}
|
|
30
37
|
|
|
38
|
+
return await createClientWithCredentials(
|
|
39
|
+
{
|
|
40
|
+
endpoint: finalEndpoint,
|
|
41
|
+
username: finalUsername,
|
|
42
|
+
password: finalPassword,
|
|
43
|
+
token: savedCredentials?.token,
|
|
44
|
+
longToken: savedCredentials?.longToken,
|
|
45
|
+
secret: savedCredentials?.secret
|
|
46
|
+
},
|
|
47
|
+
timeout,
|
|
48
|
+
saveCredentials
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Create authenticated client with credentials
|
|
54
|
+
* @param {Object} credentials - Credentials object
|
|
55
|
+
* @param {string} credentials.endpoint - Server endpoint
|
|
56
|
+
* @param {string} credentials.username - Username
|
|
57
|
+
* @param {string} credentials.password - Password
|
|
58
|
+
* @param {number} timeout - Connection timeout in seconds
|
|
59
|
+
* @param {boolean} saveToSettings - Whether to save credentials to settings
|
|
60
|
+
* @returns {Promise<FnosClient>} Authenticated client
|
|
61
|
+
*/
|
|
62
|
+
async function createClientWithCredentials(credentials, timeout, saveToSettings) {
|
|
63
|
+
const { endpoint, username, password } = credentials;
|
|
64
|
+
|
|
31
65
|
// Create client
|
|
32
66
|
const client = new FnosClient();
|
|
33
|
-
logger.info(`Connecting to ${
|
|
67
|
+
logger.info(`Connecting to ${endpoint}...`);
|
|
34
68
|
|
|
35
69
|
// Connect
|
|
36
|
-
await client.connect(
|
|
70
|
+
await client.connect(endpoint, timeout * 1000);
|
|
37
71
|
logger.info('Connected successfully');
|
|
38
72
|
|
|
39
|
-
// Try to login with token first
|
|
40
|
-
// if (savedCredentials?.token && savedCredentials?.secret && !endpoint && !username && !password) {
|
|
41
|
-
// try {
|
|
42
|
-
// logger.info('Logging in with saved token...');
|
|
43
|
-
// await client.loginViaToken(savedCredentials.token, savedCredentials.longToken, savedCredentials.secret);
|
|
44
|
-
// logger.info('Logged in successfully with token');
|
|
45
|
-
// return client;
|
|
46
|
-
// } catch (error) {
|
|
47
|
-
// logger.warn('Token login failed, falling back to password login:', error.message);
|
|
48
|
-
// }
|
|
49
|
-
// }
|
|
50
|
-
|
|
51
73
|
// Login with password
|
|
52
|
-
if (!
|
|
74
|
+
if (!password) {
|
|
53
75
|
throw new Error('Password required. Please run "fnos login" first or provide -p parameter.');
|
|
54
76
|
}
|
|
55
77
|
|
|
56
78
|
logger.info('Logging in...');
|
|
57
|
-
const loginResult = await client.login(
|
|
79
|
+
const loginResult = await client.login(username, password);
|
|
58
80
|
logger.info('Logged in successfully');
|
|
59
81
|
|
|
60
|
-
// Save credentials if
|
|
61
|
-
if (
|
|
82
|
+
// Save credentials only if explicitly requested (e.g., from login command)
|
|
83
|
+
if (saveToSettings) {
|
|
62
84
|
settings.saveCredentials({
|
|
63
|
-
endpoint
|
|
64
|
-
username
|
|
65
|
-
password
|
|
85
|
+
endpoint,
|
|
86
|
+
username,
|
|
87
|
+
password,
|
|
66
88
|
token: loginResult.token,
|
|
67
89
|
longToken: loginResult.longToken,
|
|
68
90
|
secret: loginResult.secret
|
package/src/utils/settings.js
CHANGED
|
@@ -63,6 +63,23 @@ class Settings {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Clear only authentication credentials, preserving other settings
|
|
68
|
+
*/
|
|
69
|
+
clearCredentials() {
|
|
70
|
+
const existingSettings = this.load();
|
|
71
|
+
if (!existingSettings) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Remove only auth-related fields, keep other settings
|
|
76
|
+
const authFields = ['endpoint', 'username', 'password', 'token', 'longToken', 'secret'];
|
|
77
|
+
authFields.forEach(field => delete existingSettings[field]);
|
|
78
|
+
|
|
79
|
+
// Always save the file (even if empty), to preserve the settings.json file
|
|
80
|
+
this.save(existingSettings);
|
|
81
|
+
}
|
|
82
|
+
|
|
66
83
|
/**
|
|
67
84
|
* Check if settings file exists
|
|
68
85
|
* @returns {boolean} True if settings file exists
|