quick-sh 1.0.0 → 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.
- package/CHANGELOG.md +20 -0
- package/LICENSE +1 -1
- package/README.md +6 -6
- package/bin/cli.js +152 -8
- package/lib/ai.js +619 -0
- package/lib/config.js +61 -4
- package/lib/help.js +34 -52
- package/lib/i18n.js +153 -0
- package/lib/remote-manager.js +317 -0
- package/lib/script-manager.js +348 -48
- package/locales/en.json +251 -0
- package/locales/ja.json +251 -0
- package/locales/zh.json +251 -0
- package/package.json +15 -7
package/lib/script-manager.js
CHANGED
|
@@ -3,31 +3,37 @@ const path = require('path');
|
|
|
3
3
|
const { readConfig, readAliasConfig } = require('./config');
|
|
4
4
|
const { executeFile, executeFromDirectory, executeSystemCommand } = require('./executor');
|
|
5
5
|
const { checkSystemCommand } = require('./utils');
|
|
6
|
+
const { findRemoteScript, getRemoteScriptsDir, getSources } = require('./remote-manager');
|
|
7
|
+
const { t } = require('./i18n');
|
|
6
8
|
|
|
7
9
|
// 执行脚本(核心逻辑,包含alias处理和优先级)
|
|
8
10
|
async function executeScript(scriptName, args = []) {
|
|
9
11
|
const config = await readConfig();
|
|
10
12
|
|
|
11
13
|
if (!config.scriptPath) {
|
|
12
|
-
console.error('
|
|
14
|
+
console.error(t('commands.noPathConfigured'));
|
|
13
15
|
process.exit(1);
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
// 优先级1: 检查alias配置
|
|
17
19
|
const aliasConfig = await readAliasConfig(config.scriptPath);
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
|
|
21
|
+
// 支持两种格式:直接在根级别定义aliases,或在aliases字段下定义
|
|
22
|
+
const aliases = aliasConfig.aliases || aliasConfig;
|
|
23
|
+
|
|
24
|
+
if (aliases[scriptName]) {
|
|
25
|
+
const aliasValue = aliases[scriptName];
|
|
20
26
|
|
|
21
27
|
if (typeof aliasValue === 'string') {
|
|
22
28
|
// 字符串值:可能是绝对路径或系统命令
|
|
23
29
|
if (path.isAbsolute(aliasValue)) {
|
|
24
30
|
// 绝对路径
|
|
25
31
|
if (await fs.pathExists(aliasValue)) {
|
|
26
|
-
console.log(
|
|
32
|
+
console.log(t('commands.executingAlias', { type: 'absolute', path: aliasValue }));
|
|
27
33
|
await executeFile(aliasValue, args);
|
|
28
34
|
return;
|
|
29
35
|
} else {
|
|
30
|
-
console.error(
|
|
36
|
+
console.error(t('commands.aliasNotFound', { path: aliasValue }));
|
|
31
37
|
process.exit(1);
|
|
32
38
|
}
|
|
33
39
|
} else {
|
|
@@ -36,25 +42,44 @@ async function executeScript(scriptName, args = []) {
|
|
|
36
42
|
await executeSystemCommand(aliasValue, args);
|
|
37
43
|
return;
|
|
38
44
|
} else {
|
|
39
|
-
console.error(
|
|
45
|
+
console.error(t('commands.systemCommandNotFound', { command: aliasValue }));
|
|
40
46
|
process.exit(1);
|
|
41
47
|
}
|
|
42
48
|
}
|
|
43
49
|
} else if (aliasValue && typeof aliasValue === 'object' && aliasValue.bin) {
|
|
44
|
-
//
|
|
45
|
-
const
|
|
46
|
-
|
|
50
|
+
// 对象值:可能是绝对路径或相对路径配置
|
|
51
|
+
const binPath = aliasValue.bin;
|
|
52
|
+
let fullPath;
|
|
53
|
+
|
|
54
|
+
if (path.isAbsolute(binPath)) {
|
|
55
|
+
// 绝对路径
|
|
56
|
+
fullPath = binPath;
|
|
57
|
+
} else {
|
|
58
|
+
// 相对路径
|
|
59
|
+
fullPath = path.resolve(config.scriptPath, binPath);
|
|
60
|
+
}
|
|
47
61
|
|
|
48
62
|
if (await fs.pathExists(fullPath)) {
|
|
49
|
-
console.log(
|
|
50
|
-
|
|
63
|
+
console.log(t('commands.executingAlias', {
|
|
64
|
+
type: path.isAbsolute(binPath) ? 'absolute' : 'relative',
|
|
65
|
+
path: binPath
|
|
66
|
+
}));
|
|
67
|
+
|
|
68
|
+
// 检查是否是脚本文件(有扩展名)还是系统可执行文件
|
|
69
|
+
const ext = path.extname(fullPath);
|
|
70
|
+
if (ext === '.js' || ext === '.mjs' || ext === '.sh') {
|
|
71
|
+
await executeFile(fullPath, args);
|
|
72
|
+
} else {
|
|
73
|
+
// 对于没有扩展名的可执行文件,使用系统命令执行
|
|
74
|
+
await executeSystemCommand(fullPath, args);
|
|
75
|
+
}
|
|
51
76
|
return;
|
|
52
77
|
} else {
|
|
53
|
-
console.error(
|
|
78
|
+
console.error(t('commands.aliasNotFound', { path: fullPath }));
|
|
54
79
|
process.exit(1);
|
|
55
80
|
}
|
|
56
81
|
} else {
|
|
57
|
-
console.error(
|
|
82
|
+
console.error(t('commands.invalidAlias', { name: scriptName }));
|
|
58
83
|
process.exit(1);
|
|
59
84
|
}
|
|
60
85
|
}
|
|
@@ -89,65 +114,340 @@ async function executeScript(scriptName, args = []) {
|
|
|
89
114
|
return;
|
|
90
115
|
}
|
|
91
116
|
|
|
92
|
-
// 优先级3:
|
|
117
|
+
// 优先级3: 检查远程下载的脚本
|
|
118
|
+
const remoteScript = await findRemoteScript(scriptName);
|
|
119
|
+
if (remoteScript) {
|
|
120
|
+
console.log(t('commands.executingRemote', {
|
|
121
|
+
script: `${remoteScript.sourceName}/${path.basename(remoteScript.scriptPath)}`
|
|
122
|
+
}));
|
|
123
|
+
await executeFile(remoteScript.scriptPath, args);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// 优先级4: 检查系统可执行命令
|
|
93
128
|
if (await checkSystemCommand(scriptName)) {
|
|
94
129
|
await executeSystemCommand(scriptName, args);
|
|
95
130
|
return;
|
|
96
131
|
}
|
|
97
132
|
|
|
98
|
-
console.error(
|
|
99
|
-
console.error(
|
|
100
|
-
console.error(
|
|
101
|
-
console.error(
|
|
102
|
-
console.error(
|
|
133
|
+
console.error(t('commands.commandNotFound', { command: scriptName }));
|
|
134
|
+
console.error(t('commands.lookedIn'));
|
|
135
|
+
console.error(t('commands.aliasConfig', { path: path.join(config.scriptPath, 'config.json') }));
|
|
136
|
+
console.error(t('commands.scriptDirectory', { path: config.scriptPath }));
|
|
137
|
+
console.error(t('commands.remoteScripts', { path: getRemoteScriptsDir() }));
|
|
138
|
+
console.error(t('commands.systemPath'));
|
|
103
139
|
process.exit(1);
|
|
104
140
|
}
|
|
105
141
|
|
|
142
|
+
// 从package.json读取描述信息
|
|
143
|
+
async function getPackageDescription(dirPath) {
|
|
144
|
+
try {
|
|
145
|
+
const packagePath = path.join(dirPath, 'package.json');
|
|
146
|
+
if (await fs.pathExists(packagePath)) {
|
|
147
|
+
const packageJson = await fs.readJson(packagePath);
|
|
148
|
+
return packageJson.description || '';
|
|
149
|
+
}
|
|
150
|
+
} catch (error) {
|
|
151
|
+
// 忽略读取错误
|
|
152
|
+
}
|
|
153
|
+
return '';
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 从脚本文件头部注释读取描述信息
|
|
157
|
+
async function getScriptDescription(filePath) {
|
|
158
|
+
try {
|
|
159
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
160
|
+
const lines = content.split('\n').slice(0, 10); // 只读取前10行
|
|
161
|
+
|
|
162
|
+
// 查找描述注释
|
|
163
|
+
for (const line of lines) {
|
|
164
|
+
const trimmed = line.trim();
|
|
165
|
+
|
|
166
|
+
// JavaScript风格注释
|
|
167
|
+
if (trimmed.startsWith('// Description:') || trimmed.startsWith('// @description')) {
|
|
168
|
+
return trimmed.replace(/^\/\/ (?:Description:|@description)\s*/, '');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Shell风格注释
|
|
172
|
+
if (trimmed.startsWith('# Description:') || trimmed.startsWith('# @description')) {
|
|
173
|
+
return trimmed.replace(/^# (?:Description:|@description)\s*/, '');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// 通用描述格式
|
|
177
|
+
if (trimmed.includes('Description:')) {
|
|
178
|
+
const match = trimmed.match(/Description:\s*(.+)$/);
|
|
179
|
+
if (match) return match[1];
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
} catch (error) {
|
|
183
|
+
// 忽略读取错误
|
|
184
|
+
}
|
|
185
|
+
return '';
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// 递归扫描目录中的脚本文件
|
|
189
|
+
async function scanScripts(dir, basePath = '', maxDepth = 3, currentDepth = 0) {
|
|
190
|
+
const scripts = [];
|
|
191
|
+
|
|
192
|
+
if (currentDepth >= maxDepth) {
|
|
193
|
+
return scripts;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
const files = await fs.readdir(dir);
|
|
198
|
+
|
|
199
|
+
for (const file of files) {
|
|
200
|
+
// 跳过config.json和README文件
|
|
201
|
+
if (file === 'config.json' || file.startsWith('README') || file.startsWith('.')) {
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const filePath = path.join(dir, file);
|
|
206
|
+
const stat = await fs.stat(filePath);
|
|
207
|
+
const relativePath = basePath ? `${basePath}/${file}` : file;
|
|
208
|
+
|
|
209
|
+
if (stat.isDirectory()) {
|
|
210
|
+
// 检查目录是否包含index文件
|
|
211
|
+
const indexFiles = ['index.js', 'index.sh', 'index.mjs'];
|
|
212
|
+
let hasIndex = false;
|
|
213
|
+
|
|
214
|
+
for (const indexFile of indexFiles) {
|
|
215
|
+
if (await fs.pathExists(path.join(filePath, indexFile))) {
|
|
216
|
+
// 尝试从package.json获取描述
|
|
217
|
+
const packageDesc = await getPackageDescription(filePath);
|
|
218
|
+
// 如果没有package.json描述,尝试从index文件获取
|
|
219
|
+
const scriptDesc = packageDesc || await getScriptDescription(path.join(filePath, indexFile));
|
|
220
|
+
|
|
221
|
+
scripts.push({
|
|
222
|
+
name: relativePath,
|
|
223
|
+
type: 'directory',
|
|
224
|
+
entry: indexFile,
|
|
225
|
+
path: relativePath,
|
|
226
|
+
description: scriptDesc
|
|
227
|
+
});
|
|
228
|
+
hasIndex = true;
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// 递归扫描子目录
|
|
234
|
+
const subScripts = await scanScripts(filePath, relativePath, maxDepth, currentDepth + 1);
|
|
235
|
+
scripts.push(...subScripts);
|
|
236
|
+
|
|
237
|
+
} else if (file.endsWith('.js') || file.endsWith('.sh') || file.endsWith('.mjs')) {
|
|
238
|
+
// 从脚本文件获取描述
|
|
239
|
+
const scriptDesc = await getScriptDescription(filePath);
|
|
240
|
+
|
|
241
|
+
scripts.push({
|
|
242
|
+
name: file,
|
|
243
|
+
type: 'file',
|
|
244
|
+
path: relativePath,
|
|
245
|
+
description: scriptDesc
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
} catch (error) {
|
|
250
|
+
// 忽略无法读取的目录
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return scripts;
|
|
254
|
+
}
|
|
255
|
+
|
|
106
256
|
// 显示当前配置和可用脚本
|
|
107
257
|
async function showStatus() {
|
|
108
258
|
const config = await readConfig();
|
|
109
259
|
|
|
110
|
-
if (config.scriptPath) {
|
|
111
|
-
console.log(
|
|
260
|
+
if (!config.scriptPath) {
|
|
261
|
+
console.log(t('commands.noPathConfigured'));
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
console.log(t('status.currentPath', { path: config.scriptPath }));
|
|
266
|
+
|
|
267
|
+
// 读取别名配置
|
|
268
|
+
const aliasConfig = await readAliasConfig(config.scriptPath);
|
|
269
|
+
const aliases = aliasConfig.aliases || aliasConfig;
|
|
270
|
+
|
|
271
|
+
// 显示配置的别名
|
|
272
|
+
if (Object.keys(aliases).length > 0) {
|
|
273
|
+
console.log('\n' + t('status.configuredAliases'));
|
|
274
|
+
|
|
275
|
+
// 按类型分组别名
|
|
276
|
+
const aliasGroups = {
|
|
277
|
+
relative: [], // 相对路径
|
|
278
|
+
absolute: [], // 绝对路径
|
|
279
|
+
system: [] // 系统命令
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
for (const [name, config] of Object.entries(aliases)) {
|
|
283
|
+
let description = '';
|
|
284
|
+
let binPath = '';
|
|
285
|
+
let type = 'system';
|
|
286
|
+
|
|
287
|
+
if (typeof config === 'string') {
|
|
288
|
+
binPath = config;
|
|
289
|
+
if (path.isAbsolute(config)) {
|
|
290
|
+
type = 'absolute';
|
|
291
|
+
} else {
|
|
292
|
+
type = 'system';
|
|
293
|
+
}
|
|
294
|
+
} else if (config && typeof config === 'object') {
|
|
295
|
+
binPath = config.bin || '';
|
|
296
|
+
description = config.description || '';
|
|
297
|
+
if (binPath.startsWith('./')) {
|
|
298
|
+
type = 'relative';
|
|
299
|
+
} else if (path.isAbsolute(binPath)) {
|
|
300
|
+
type = 'absolute';
|
|
301
|
+
} else {
|
|
302
|
+
type = 'system';
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
aliasGroups[type].push({
|
|
307
|
+
name,
|
|
308
|
+
bin: binPath,
|
|
309
|
+
description
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// 显示相对路径别名(项目脚本)
|
|
314
|
+
if (aliasGroups.relative.length > 0) {
|
|
315
|
+
console.log(' ' + t('status.projectScripts'));
|
|
316
|
+
aliasGroups.relative.forEach(alias => {
|
|
317
|
+
const desc = alias.description ? ` - ${alias.description}` : '';
|
|
318
|
+
console.log(` • ${alias.name.padEnd(15)} ${alias.bin}${desc}`);
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// 显示绝对路径别名
|
|
323
|
+
if (aliasGroups.absolute.length > 0) {
|
|
324
|
+
console.log(' ' + t('status.absolutePathScripts'));
|
|
325
|
+
aliasGroups.absolute.forEach(alias => {
|
|
326
|
+
const desc = alias.description ? ` - ${alias.description}` : '';
|
|
327
|
+
console.log(` • ${alias.name.padEnd(15)} ${alias.bin}${desc}`);
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// 显示系统命令别名
|
|
332
|
+
if (aliasGroups.system.length > 0) {
|
|
333
|
+
console.log(' ' + t('status.systemCommandAliases'));
|
|
334
|
+
aliasGroups.system.forEach(alias => {
|
|
335
|
+
const desc = alias.description ? ` - ${alias.description}` : '';
|
|
336
|
+
console.log(` • ${alias.name.padEnd(15)} ${alias.bin}${desc}`);
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// 扫描并显示所有脚本文件
|
|
342
|
+
try {
|
|
343
|
+
const scripts = await scanScripts(config.scriptPath);
|
|
112
344
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const files = await fs.readdir(config.scriptPath);
|
|
116
|
-
const scripts = [];
|
|
345
|
+
if (scripts.length > 0) {
|
|
346
|
+
console.log('\n' + t('status.availableScripts'));
|
|
117
347
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
348
|
+
// 按目录分组
|
|
349
|
+
const scriptsByDir = {};
|
|
350
|
+
scripts.forEach(script => {
|
|
351
|
+
const dir = path.dirname(script.path);
|
|
352
|
+
const dirName = dir === '.' ? 'root' : dir;
|
|
121
353
|
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
354
|
+
if (!scriptsByDir[dirName]) {
|
|
355
|
+
scriptsByDir[dirName] = [];
|
|
356
|
+
}
|
|
357
|
+
scriptsByDir[dirName].push(script);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// 显示每个目录的脚本
|
|
361
|
+
for (const [dirName, dirScripts] of Object.entries(scriptsByDir)) {
|
|
362
|
+
if (dirName === 'root') {
|
|
363
|
+
console.log(' ' + t('status.rootDirectory'));
|
|
364
|
+
} else {
|
|
365
|
+
console.log(` 📁 ${dirName}/:`);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
dirScripts.forEach(script => {
|
|
369
|
+
const description = script.description ? ` - ${script.description}` : '';
|
|
370
|
+
|
|
371
|
+
if (script.type === 'directory') {
|
|
372
|
+
console.log(` 📁 ${script.name}/ (${script.entry})${description}`);
|
|
373
|
+
} else {
|
|
374
|
+
const ext = path.extname(script.name);
|
|
375
|
+
const icon = ext === '.js' || ext === '.mjs' ? '📄' : ext === '.sh' ? '📜' : '📄';
|
|
376
|
+
console.log(` ${icon} ${script.name.padEnd(20)}${description}`);
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
} else {
|
|
381
|
+
console.log('\n' + t('status.noScriptsFound'));
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
} catch (error) {
|
|
385
|
+
console.error(t('status.errorScanning', { error: error.message }));
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// 显示远程脚本
|
|
389
|
+
try {
|
|
390
|
+
const sources = await getSources();
|
|
391
|
+
if (Object.keys(sources).length > 0) {
|
|
392
|
+
console.log('\n' + t('status.remoteScriptsTitle'));
|
|
393
|
+
|
|
394
|
+
let hasRemoteScripts = false;
|
|
395
|
+
for (const [sourceName, source] of Object.entries(sources)) {
|
|
396
|
+
const sourceDir = path.join(getRemoteScriptsDir(), sourceName);
|
|
397
|
+
if (await fs.pathExists(sourceDir)) {
|
|
398
|
+
const files = await fs.readdir(sourceDir);
|
|
399
|
+
const scripts = files.filter(f => !f.startsWith('.'));
|
|
400
|
+
|
|
401
|
+
if (scripts.length > 0) {
|
|
402
|
+
console.log(` 📡 ${sourceName} (${source.type}):`);
|
|
403
|
+
scripts.sort().forEach(script => {
|
|
404
|
+
const ext = path.extname(script);
|
|
405
|
+
const icon = ext === '.js' || ext === '.mjs' ? '📄' : ext === '.sh' ? '📜' : '📄';
|
|
406
|
+
console.log(` ${icon} ${script}`);
|
|
407
|
+
});
|
|
408
|
+
hasRemoteScripts = true;
|
|
130
409
|
}
|
|
131
|
-
} else if (file.endsWith('.js') || file.endsWith('.sh') || file.endsWith('.mjs')) {
|
|
132
|
-
scripts.push(file);
|
|
133
410
|
}
|
|
134
411
|
}
|
|
135
412
|
|
|
136
|
-
if (
|
|
137
|
-
console.log('
|
|
138
|
-
|
|
139
|
-
} else {
|
|
140
|
-
console.log('\nNo executable scripts found.');
|
|
413
|
+
if (!hasRemoteScripts) {
|
|
414
|
+
console.log(' ' + t('status.noRemoteScripts'));
|
|
415
|
+
console.log(' ' + t('status.downloadTip'));
|
|
141
416
|
}
|
|
142
|
-
} catch (error) {
|
|
143
|
-
console.error('Error reading script directory:', error.message);
|
|
144
417
|
}
|
|
145
|
-
}
|
|
146
|
-
|
|
418
|
+
} catch (error) {
|
|
419
|
+
// 忽略远程脚本显示错误
|
|
147
420
|
}
|
|
421
|
+
|
|
422
|
+
// 显示使用提示
|
|
423
|
+
console.log('\n' + t('status.usageTips'));
|
|
424
|
+
console.log(' ' + t('status.useAliases'));
|
|
425
|
+
console.log(' ' + t('status.useFileNames'));
|
|
426
|
+
console.log(' ' + t('status.useDirectories'));
|
|
427
|
+
console.log(' ' + t('status.useRemote'));
|
|
428
|
+
console.log(' ' + t('status.getHelp'));
|
|
429
|
+
console.log(' ' + t('status.manageSources'));
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// 显示简洁的软件介绍
|
|
433
|
+
function showBrief() {
|
|
434
|
+
console.log(`\n🚀 ${t('app.name')}`);
|
|
435
|
+
console.log(`${t('app.description')}\n`);
|
|
436
|
+
|
|
437
|
+
console.log(t('help.usage'));
|
|
438
|
+
console.log(` q <script> ${t('help.executeScript')}`);
|
|
439
|
+
console.log(` q -l ${t('help.listScripts')}`);
|
|
440
|
+
console.log(` q -h, --help ${t('help.showHelp')}`);
|
|
441
|
+
console.log(` q -path <dir> ${t('help.setDirectory')}\n`);
|
|
442
|
+
|
|
443
|
+
console.log(t('help.examples'));
|
|
444
|
+
console.log(` q hello ${t('help.runScript', { script: 'hello' })}`);
|
|
445
|
+
console.log(` q -l ${t('help.listScripts')}`);
|
|
446
|
+
console.log(` q --help ${t('help.showHelp')}\n`);
|
|
148
447
|
}
|
|
149
448
|
|
|
150
449
|
module.exports = {
|
|
151
450
|
executeScript,
|
|
152
|
-
showStatus
|
|
451
|
+
showStatus,
|
|
452
|
+
showBrief
|
|
153
453
|
};
|