jjb-cmd 2.5.3 → 2.5.4
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/SECURITY.md +71 -0
- package/bin/command.js +6 -0
- package/build.js +20 -0
- package/obf.config.json +3 -0
- package/package.json +4 -2
- package/src/ai-pull.js +675 -0
- package/src/auth.js +71 -1
- package/src/code-optimization.js +46 -1
- package/src/config.js +122 -1
- package/src/crypto-utils.js +183 -1
- package/src/password-input.js +79 -1
- package/src/publish.js +307 -1
- package/src/push.js +417 -1
- package/src/rm-rf.js +49 -1
- package/src/utils.js +228 -1
package/src/auth.js
CHANGED
|
@@ -1 +1,71 @@
|
|
|
1
|
-
const
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { getApiHost } = require('./config');
|
|
4
|
+
const {
|
|
5
|
+
logInfo,
|
|
6
|
+
logSuccess,
|
|
7
|
+
logError,
|
|
8
|
+
fileExists,
|
|
9
|
+
deleteFile
|
|
10
|
+
} = require('./utils');
|
|
11
|
+
const { saveAuth, deleteAuth } = require('./crypto-utils');
|
|
12
|
+
const { getCredentials, interactiveAuth } = require('./password-input');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 用户认证模块
|
|
16
|
+
* @param {string[]} args - 命令行参数(不再使用)
|
|
17
|
+
*/
|
|
18
|
+
module.exports = async (args) => {
|
|
19
|
+
const authPath = path.join(__dirname, '../.auth');
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
// 强制使用完全交互式模式
|
|
23
|
+
logInfo('启动交互式登录流程');
|
|
24
|
+
const credentials = await interactiveAuth();
|
|
25
|
+
const username = credentials.username;
|
|
26
|
+
const password = credentials.password;
|
|
27
|
+
|
|
28
|
+
logInfo('正在验证用户凭证...');
|
|
29
|
+
|
|
30
|
+
// 验证登录
|
|
31
|
+
const response = await axios.post(`${getApiHost()}/api/auth`, {
|
|
32
|
+
username,
|
|
33
|
+
password
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (response.data.status) {
|
|
37
|
+
logSuccess('用户认证成功');
|
|
38
|
+
try {
|
|
39
|
+
saveAuth(username, password, authPath);
|
|
40
|
+
logSuccess('认证信息已安全加密存储');
|
|
41
|
+
} catch (error) {
|
|
42
|
+
logError(`保存认证信息失败: ${error.message}`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
logError(`认证失败: ${response.data.message || '未知错误'}`);
|
|
47
|
+
if (fileExists(authPath)) {
|
|
48
|
+
deleteAuth(authPath);
|
|
49
|
+
logInfo('已清理无效的认证信息');
|
|
50
|
+
}
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
} catch (error) {
|
|
54
|
+
if (error.message.includes('用户取消输入')) {
|
|
55
|
+
logError('用户取消登录操作');
|
|
56
|
+
} else if (error.response) {
|
|
57
|
+
const status = error.response.status;
|
|
58
|
+
const message = error.response.data?.message || error.message;
|
|
59
|
+
if (status === 401 || status === 403) {
|
|
60
|
+
logError(`认证失败 (${status}): 用户名或密码错误`);
|
|
61
|
+
} else {
|
|
62
|
+
logError(`请求失败 (${status}): ${message}`);
|
|
63
|
+
}
|
|
64
|
+
} else if (error.request) {
|
|
65
|
+
logError(`网络连接失败: 无法连接到服务器`);
|
|
66
|
+
} else {
|
|
67
|
+
logError(`登录失败: ${error.message}`);
|
|
68
|
+
}
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
};
|
package/src/code-optimization.js
CHANGED
|
@@ -1 +1,46 @@
|
|
|
1
|
-
const
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const inquirer = require('inquirer');
|
|
3
|
+
const { bootstrap } = require('@cqsjjb/react-code-optimization');
|
|
4
|
+
const {
|
|
5
|
+
logInfo,
|
|
6
|
+
logSuccess,
|
|
7
|
+
logError,
|
|
8
|
+
logWarning
|
|
9
|
+
} = require('./utils');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 代码优化模块
|
|
13
|
+
* 使用 @cqsjjb/react-code-optimization 进行代码优化
|
|
14
|
+
*/
|
|
15
|
+
module.exports = async function () {
|
|
16
|
+
const rootPath = path.resolve('./');
|
|
17
|
+
|
|
18
|
+
logInfo('代码优化工具已启动');
|
|
19
|
+
logWarning('此操作将优化当前目录下的代码,请确保已备份重要文件');
|
|
20
|
+
|
|
21
|
+
const { confirmed } = await inquirer.prompt([
|
|
22
|
+
{
|
|
23
|
+
type: 'confirm',
|
|
24
|
+
name: 'confirmed',
|
|
25
|
+
message: '是否确认优化代码?',
|
|
26
|
+
default: false
|
|
27
|
+
}
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
if (confirmed) {
|
|
31
|
+
logInfo('正在分析代码结构...');
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
bootstrap(rootPath, 100, () => {
|
|
35
|
+
logSuccess('代码优化已完成');
|
|
36
|
+
process.exit(0);
|
|
37
|
+
});
|
|
38
|
+
} catch (error) {
|
|
39
|
+
logError(`代码优化失败: ${error.message}`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
logInfo('操作已取消');
|
|
44
|
+
process.exit(0);
|
|
45
|
+
}
|
|
46
|
+
};
|
package/src/config.js
CHANGED
|
@@ -1 +1,122 @@
|
|
|
1
|
-
|
|
1
|
+
const os = require('os');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* API 主机配置
|
|
7
|
+
*/
|
|
8
|
+
const API_HOST = 'http://120.26.210.58:8088';
|
|
9
|
+
const API_HOST_HTTPS = 'https://jcloud.cqjjb.cn';
|
|
10
|
+
const API_HOST_TEST = 'http://120.26.210.58:8089';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 配置文件主机地址
|
|
14
|
+
*/
|
|
15
|
+
exports.CONFIG_FILE_HOST = 'https://cdn.cqjjb.cn/jjb-cloud-config';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* API 主机地址
|
|
19
|
+
*/
|
|
20
|
+
exports.API_HOST = API_HOST;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 测试环境 API 主机地址
|
|
24
|
+
*/
|
|
25
|
+
exports.API_HOST_TEST = API_HOST_TEST;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Git 主机地址
|
|
29
|
+
*/
|
|
30
|
+
exports.GIT_HOST = 'http://192.168.1.242:10985';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 临时目录名称
|
|
34
|
+
*/
|
|
35
|
+
exports.GIT_TEMP_DIR = `jjb-assembly-${Date.now()}`;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Java 临时目录名称
|
|
39
|
+
*/
|
|
40
|
+
exports.GIT_TEMP_JAVA = `jjb-assembly-java`;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Java 环境映射
|
|
44
|
+
*/
|
|
45
|
+
exports.GIT_JAVA_ENV_JSON = {
|
|
46
|
+
development: 'dev',
|
|
47
|
+
production: 'prod',
|
|
48
|
+
test: 'test'
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* 云项目配置
|
|
52
|
+
* 包含各个项目的 token 和 projectId
|
|
53
|
+
*/
|
|
54
|
+
exports.CLOUD_PROJECT = {
|
|
55
|
+
common: {
|
|
56
|
+
token: 'G4HJRsHr9D7Ssmixegw2',
|
|
57
|
+
projectId: 279
|
|
58
|
+
},
|
|
59
|
+
'react-admin-component': {
|
|
60
|
+
token: 'FT3pKzxpRynFkmddJ9Bs',
|
|
61
|
+
projectId: 340
|
|
62
|
+
},
|
|
63
|
+
'jjb-dva-runtime': {
|
|
64
|
+
token: 'hLqARY89CN6fUD3yg4NL',
|
|
65
|
+
projectId: 571
|
|
66
|
+
},
|
|
67
|
+
'jjb-common-lib': {
|
|
68
|
+
token: 'e9njpBd1nS_LREN8GFpR',
|
|
69
|
+
projectId: 572
|
|
70
|
+
},
|
|
71
|
+
'jjb-common-decorator': {
|
|
72
|
+
token: 'gPSit8aJsLVmNzuQ5Cy4',
|
|
73
|
+
projectId: 574
|
|
74
|
+
},
|
|
75
|
+
'vue-unisass-component': {
|
|
76
|
+
token: 'd4wQ7dzEjYPsgVbKnYei',
|
|
77
|
+
projectId: 339
|
|
78
|
+
},
|
|
79
|
+
'react-component': {
|
|
80
|
+
token: 'snBxJ2i5kYarGGcsojhY',
|
|
81
|
+
projectId: 831
|
|
82
|
+
},
|
|
83
|
+
'micro-app-ts': {
|
|
84
|
+
token: '7V-YUxhmh51Mdhgx4rq4',
|
|
85
|
+
projectId: 830
|
|
86
|
+
},
|
|
87
|
+
'micro-app': {
|
|
88
|
+
token: 'FqNrmFAgrxasMrbbjvq9',
|
|
89
|
+
projectId: 829
|
|
90
|
+
},
|
|
91
|
+
lib: {
|
|
92
|
+
token: 'ywPtT3xCG6b_vAxp6sTj',
|
|
93
|
+
projectId: 828
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* 模板文件夹路径
|
|
99
|
+
*/
|
|
100
|
+
exports.TEMPLATE_FOLDER = path.join(os.tmpdir(), exports.GIT_TEMP_DIR);
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 获取 API 主机地址
|
|
104
|
+
* 根据 package.json 中的配置决定使用哪个 API 主机
|
|
105
|
+
* @returns {string} API 主机地址
|
|
106
|
+
*/
|
|
107
|
+
exports.getApiHost = () => {
|
|
108
|
+
try {
|
|
109
|
+
const packageJson = JSON.parse(
|
|
110
|
+
fs.readFileSync(path.join(__dirname, '../package.json')).toString()
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
if (packageJson.env === 'test') {
|
|
114
|
+
return API_HOST_TEST;
|
|
115
|
+
} else {
|
|
116
|
+
return packageJson.httpMethod === 'http' ? API_HOST : API_HOST_HTTPS;
|
|
117
|
+
}
|
|
118
|
+
} catch (error) {
|
|
119
|
+
// 如果读取 package.json 失败,默认返回 HTTPS 地址
|
|
120
|
+
return API_HOST_HTTPS;
|
|
121
|
+
}
|
|
122
|
+
};
|
package/src/crypto-utils.js
CHANGED
|
@@ -1 +1,183 @@
|
|
|
1
|
-
const
|
|
1
|
+
const crypto = require('crypto');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 加密配置
|
|
7
|
+
*/
|
|
8
|
+
const CRYPTO_CONFIG = {
|
|
9
|
+
algorithm: 'aes-256-cbc',
|
|
10
|
+
keyLength: 32, // 256 bits
|
|
11
|
+
ivLength: 16, // 128 bits
|
|
12
|
+
saltLength: 32 // 256 bits
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 生成加密密钥
|
|
17
|
+
* @param {string} password - 主密码
|
|
18
|
+
* @param {Buffer} salt - 盐值
|
|
19
|
+
* @returns {Buffer} 加密密钥
|
|
20
|
+
*/
|
|
21
|
+
function deriveKey(password, salt) {
|
|
22
|
+
return crypto.pbkdf2Sync(password, salt, 100000, CRYPTO_CONFIG.keyLength, 'sha512');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 生成随机盐值
|
|
27
|
+
* @returns {Buffer} 随机盐值
|
|
28
|
+
*/
|
|
29
|
+
function generateSalt() {
|
|
30
|
+
return crypto.randomBytes(CRYPTO_CONFIG.saltLength);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 生成随机IV
|
|
35
|
+
* @returns {Buffer} 随机IV
|
|
36
|
+
*/
|
|
37
|
+
function generateIV() {
|
|
38
|
+
return crypto.randomBytes(CRYPTO_CONFIG.ivLength);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 加密数据
|
|
43
|
+
* @param {string} text - 要加密的文本
|
|
44
|
+
* @param {string} password - 加密密码
|
|
45
|
+
* @returns {string} 加密后的数据(base64编码)
|
|
46
|
+
*/
|
|
47
|
+
function encrypt(text, password) {
|
|
48
|
+
try {
|
|
49
|
+
const salt = generateSalt();
|
|
50
|
+
const iv = generateIV();
|
|
51
|
+
const key = deriveKey(password, salt);
|
|
52
|
+
|
|
53
|
+
const cipher = crypto.createCipheriv(CRYPTO_CONFIG.algorithm, key, iv);
|
|
54
|
+
cipher.setAutoPadding(true);
|
|
55
|
+
|
|
56
|
+
let encrypted = cipher.update(text, 'utf8', 'hex');
|
|
57
|
+
encrypted += cipher.final('hex');
|
|
58
|
+
|
|
59
|
+
// 组合数据:salt + iv + encrypted
|
|
60
|
+
const combined = Buffer.concat([salt, iv, Buffer.from(encrypted, 'hex')]);
|
|
61
|
+
|
|
62
|
+
return combined.toString('base64');
|
|
63
|
+
} catch (error) {
|
|
64
|
+
throw new Error(`加密失败: ${error.message}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 解密数据
|
|
70
|
+
* @param {string} encryptedData - 加密的数据(base64编码)
|
|
71
|
+
* @param {string} password - 解密密码
|
|
72
|
+
* @returns {string} 解密后的文本
|
|
73
|
+
*/
|
|
74
|
+
function decrypt(encryptedData, password) {
|
|
75
|
+
try {
|
|
76
|
+
const combined = Buffer.from(encryptedData, 'base64');
|
|
77
|
+
|
|
78
|
+
// 分离数据
|
|
79
|
+
const salt = combined.slice(0, CRYPTO_CONFIG.saltLength);
|
|
80
|
+
const iv = combined.slice(CRYPTO_CONFIG.saltLength, CRYPTO_CONFIG.saltLength + CRYPTO_CONFIG.ivLength);
|
|
81
|
+
const encrypted = combined.slice(CRYPTO_CONFIG.saltLength + CRYPTO_CONFIG.ivLength);
|
|
82
|
+
|
|
83
|
+
const key = deriveKey(password, salt);
|
|
84
|
+
|
|
85
|
+
const decipher = crypto.createDecipheriv(CRYPTO_CONFIG.algorithm, key, iv);
|
|
86
|
+
decipher.setAutoPadding(true);
|
|
87
|
+
|
|
88
|
+
let decrypted = decipher.update(encrypted, null, 'utf8');
|
|
89
|
+
decrypted += decipher.final('utf8');
|
|
90
|
+
|
|
91
|
+
return decrypted;
|
|
92
|
+
} catch (error) {
|
|
93
|
+
throw new Error(`解密失败: ${error.message}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* 生成设备特定的加密密钥
|
|
99
|
+
* 基于系统信息生成,确保密钥的唯一性
|
|
100
|
+
* @returns {string} 设备密钥
|
|
101
|
+
*/
|
|
102
|
+
function generateDeviceKey() {
|
|
103
|
+
const os = require('os');
|
|
104
|
+
const machineInfo = [
|
|
105
|
+
os.platform(),
|
|
106
|
+
os.arch(),
|
|
107
|
+
os.hostname(),
|
|
108
|
+
os.userInfo().username || 'unknown'
|
|
109
|
+
].join('|');
|
|
110
|
+
|
|
111
|
+
return crypto.createHash('sha256').update(machineInfo).digest('hex');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* 安全存储认证信息
|
|
116
|
+
* @param {string} username - 用户名
|
|
117
|
+
* @param {string} password - 密码
|
|
118
|
+
* @param {string} authPath - 认证文件路径
|
|
119
|
+
*/
|
|
120
|
+
function saveAuth(username, password, authPath) {
|
|
121
|
+
try {
|
|
122
|
+
const deviceKey = generateDeviceKey();
|
|
123
|
+
const authData = `${username}/${password}`;
|
|
124
|
+
const encryptedData = encrypt(authData, deviceKey);
|
|
125
|
+
|
|
126
|
+
// 设置文件权限为仅当前用户可读写
|
|
127
|
+
fs.writeFileSync(authPath, encryptedData, { mode: 0o600 });
|
|
128
|
+
|
|
129
|
+
return true;
|
|
130
|
+
} catch (error) {
|
|
131
|
+
throw new Error(`保存认证信息失败: ${error.message}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 安全读取认证信息
|
|
137
|
+
* @param {string} authPath - 认证文件路径
|
|
138
|
+
* @returns {Object} 包含用户名和密码的对象
|
|
139
|
+
*/
|
|
140
|
+
function loadAuth(authPath) {
|
|
141
|
+
try {
|
|
142
|
+
if (!fs.existsSync(authPath)) {
|
|
143
|
+
throw new Error('认证文件不存在');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const deviceKey = generateDeviceKey();
|
|
147
|
+
const encryptedData = fs.readFileSync(authPath, 'utf8');
|
|
148
|
+
const decryptedData = decrypt(encryptedData, deviceKey);
|
|
149
|
+
|
|
150
|
+
const [username, password] = decryptedData.split('/');
|
|
151
|
+
|
|
152
|
+
if (!username || !password) {
|
|
153
|
+
throw new Error('认证数据格式错误');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return { username, password };
|
|
157
|
+
} catch (error) {
|
|
158
|
+
throw new Error(`读取认证信息失败: ${error.message}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* 删除认证文件
|
|
164
|
+
* @param {string} authPath - 认证文件路径
|
|
165
|
+
*/
|
|
166
|
+
function deleteAuth(authPath) {
|
|
167
|
+
try {
|
|
168
|
+
if (fs.existsSync(authPath)) {
|
|
169
|
+
fs.unlinkSync(authPath);
|
|
170
|
+
}
|
|
171
|
+
} catch (error) {
|
|
172
|
+
throw new Error(`删除认证文件失败: ${error.message}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
module.exports = {
|
|
177
|
+
encrypt,
|
|
178
|
+
decrypt,
|
|
179
|
+
saveAuth,
|
|
180
|
+
loadAuth,
|
|
181
|
+
deleteAuth,
|
|
182
|
+
generateDeviceKey
|
|
183
|
+
};
|
package/src/password-input.js
CHANGED
|
@@ -1 +1,79 @@
|
|
|
1
|
-
|
|
1
|
+
const inquirer = require('inquirer');
|
|
2
|
+
const { logError } = require('./utils');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 安全密码输入工具
|
|
6
|
+
* 使用 inquirer 实现隐藏密码输入,防止在控制台显示明文密码
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 安全获取用户名和密码
|
|
11
|
+
* @param {string} username - 用户名(如果已提供)
|
|
12
|
+
* @returns {Promise<Object>} 包含用户名和密码的对象
|
|
13
|
+
*/
|
|
14
|
+
async function getCredentials(username = null) {
|
|
15
|
+
try {
|
|
16
|
+
let finalUsername = username;
|
|
17
|
+
const prompts = [];
|
|
18
|
+
|
|
19
|
+
// 如果没有提供用户名,则提示输入
|
|
20
|
+
if (!finalUsername) {
|
|
21
|
+
prompts.push({
|
|
22
|
+
type: 'input',
|
|
23
|
+
name: 'username',
|
|
24
|
+
message: '请输入账号:',
|
|
25
|
+
validate: (input) => {
|
|
26
|
+
if (!input || !input.trim()) {
|
|
27
|
+
return '账号不能为空';
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 获取密码(使用 inquirer 的 password 类型隐藏输入)
|
|
35
|
+
prompts.push({
|
|
36
|
+
type: 'password',
|
|
37
|
+
name: 'password',
|
|
38
|
+
message: '请输入密码:',
|
|
39
|
+
mask: '*',
|
|
40
|
+
validate: (input) => {
|
|
41
|
+
if (!input || !input.trim()) {
|
|
42
|
+
return '密码不能为空';
|
|
43
|
+
}
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const answers = await inquirer.prompt(prompts);
|
|
49
|
+
|
|
50
|
+
finalUsername = finalUsername || answers.username.trim();
|
|
51
|
+
const password = answers.password;
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
username: finalUsername,
|
|
55
|
+
password: password
|
|
56
|
+
};
|
|
57
|
+
} catch (error) {
|
|
58
|
+
if (error.isTtyError) {
|
|
59
|
+
logError(`获取认证信息失败: 当前环境不支持交互式输入`);
|
|
60
|
+
} else {
|
|
61
|
+
logError(`获取认证信息失败: ${error.message}`);
|
|
62
|
+
}
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 交互式认证输入
|
|
69
|
+
* 当命令行参数不足时使用
|
|
70
|
+
* @returns {Promise<Object>} 包含用户名和密码的对象
|
|
71
|
+
*/
|
|
72
|
+
async function interactiveAuth() {
|
|
73
|
+
return await getCredentials();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
module.exports = {
|
|
77
|
+
getCredentials,
|
|
78
|
+
interactiveAuth
|
|
79
|
+
};
|