@git-ai/cli 1.0.2 → 1.0.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/LICENSE +22 -0
- package/README.md +371 -369
- package/bin/index.cjs +13 -14
- package/bin/index.mjs +40 -41
- package/package.json +73 -46
- package/src/actions/BaseAction.mjs +42 -44
- package/src/actions/BaseUrlAction.mjs +24 -24
- package/src/actions/CommitAction.mjs +432 -484
- package/src/actions/MaxTokenAction.mjs +34 -38
- package/src/actions/ModelAction.mjs +122 -131
- package/src/actions/SelectModelAction.mjs +151 -161
- package/src/actions/TokenAction.mjs +25 -25
- package/src/const.mjs +43 -41
- package/src/index.mjs +95 -69
- package/src/services/AIService.mjs +106 -113
- package/src/services/GitService.mjs +366 -388
- package/src/utils/ConflictUtils.mjs +39 -39
- package/src/utils/Log.mjs +138 -146
- package/src/utils/Logger.mjs +34 -34
- package/src/utils/MessageUtils.mjs +189 -197
- package/src/utils/OpenAI.mjs +64 -68
- package/src/utils/Spinner.mjs +39 -39
- package/src/utils/Storage.mjs +7 -7
- package/src/utils/Utils.mjs +71 -72
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import { config } from
|
|
2
|
-
import Logger from
|
|
3
|
-
import { formatToken } from
|
|
4
|
-
import BaseAction from
|
|
5
|
-
|
|
6
|
-
class SetTokenAction extends BaseAction {
|
|
7
|
-
constructor(key) {
|
|
8
|
-
super(key);
|
|
9
|
-
this.key = key ||
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
async execute() {
|
|
13
|
-
config.set(
|
|
14
|
-
if (this.key) {
|
|
15
|
-
Logger.success(`OpenAI Api Key 已设置: ${formatToken(this.key)}`);
|
|
16
|
-
} else {
|
|
17
|
-
Logger.warn(`OpenAI Api Key 已设置为空`);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export default async function (key) {
|
|
23
|
-
const action = new SetTokenAction(key);
|
|
24
|
-
await action.run();
|
|
25
|
-
}
|
|
1
|
+
import { config } from '../utils/Storage.mjs';
|
|
2
|
+
import Logger from '../utils/Logger.mjs';
|
|
3
|
+
import { formatToken } from '../utils/Utils.mjs';
|
|
4
|
+
import BaseAction from './BaseAction.mjs';
|
|
5
|
+
|
|
6
|
+
class SetTokenAction extends BaseAction {
|
|
7
|
+
constructor(key) {
|
|
8
|
+
super(key);
|
|
9
|
+
this.key = key || '';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async execute() {
|
|
13
|
+
config.set('key', this.key);
|
|
14
|
+
if (this.key) {
|
|
15
|
+
Logger.success(`OpenAI Api Key 已设置: ${formatToken(this.key)}`);
|
|
16
|
+
} else {
|
|
17
|
+
Logger.warn(`OpenAI Api Key 已设置为空`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default async function (key) {
|
|
23
|
+
const action = new SetTokenAction(key);
|
|
24
|
+
await action.run();
|
|
25
|
+
}
|
package/src/const.mjs
CHANGED
|
@@ -1,41 +1,43 @@
|
|
|
1
|
-
import { resolve } from 'path';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export const
|
|
9
|
-
export const
|
|
10
|
-
export const
|
|
11
|
-
export const
|
|
12
|
-
export const
|
|
13
|
-
export const
|
|
14
|
-
export const
|
|
15
|
-
export const
|
|
16
|
-
export const
|
|
17
|
-
export const
|
|
18
|
-
export const
|
|
19
|
-
export const
|
|
20
|
-
export const
|
|
21
|
-
export const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
1
|
+
import { resolve } from 'path';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import { readFileSync } from 'fs';
|
|
4
|
+
// 读取 package.json 文件
|
|
5
|
+
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
6
|
+
const pkgPath = resolve(__dirname, '../package.json');
|
|
7
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
8
|
+
export const BIN = Object.keys(pkg.bin)[0].split('-').join(' ');
|
|
9
|
+
export const NAME = pkg.name;
|
|
10
|
+
export const DESCRIPTION = pkg.description;
|
|
11
|
+
export const VERSION = pkg.version;
|
|
12
|
+
export const AUTHOR = pkg.author;
|
|
13
|
+
export const LOWEST_NODE_VERSION = pkg.engines.node;
|
|
14
|
+
export const OPENAI_BASE_URL = ''; //"https://api.siliconflow.cn/v1"
|
|
15
|
+
export const OPENAI_API_KEYS_URL = ''; //"https://cloud.siliconflow.cn/me/account/ak"
|
|
16
|
+
export const OPENAI_MODELS_URL = ''; //"https://cloud.siliconflow.cn/me/models"
|
|
17
|
+
export const OPENAI_MODEL_DEFAULT = ''; // "Qwen/Qwen2.5-Coder-7B-Instruct"
|
|
18
|
+
export const OPENAI_TIMEOUT = 60 * 1000 * 3;
|
|
19
|
+
export const OPENAI_MAX_TOKEN_DEFAULT = 128000;
|
|
20
|
+
export const OPENAI_MODEL_LIST_URL = process.env.OPENAI_MODEL_LIST_URL;
|
|
21
|
+
export const OPENAI_FREE_BASE_URL = 'https://open.bigmodel.cn/api/paas/v4';
|
|
22
|
+
export const OPENAI_FREE_MODEL_ID = 'glm-4-flash-250414,glm-4.5-flash';
|
|
23
|
+
export const OPENAI_COMMIT_MESSAGE_TYPES = [
|
|
24
|
+
'feat',
|
|
25
|
+
'fix',
|
|
26
|
+
'docs',
|
|
27
|
+
'style',
|
|
28
|
+
'refactor',
|
|
29
|
+
'perf',
|
|
30
|
+
'test',
|
|
31
|
+
'build',
|
|
32
|
+
'ci',
|
|
33
|
+
'chore',
|
|
34
|
+
'revert',
|
|
35
|
+
];
|
|
36
|
+
export const asciiArt = `
|
|
37
|
+
██████╗ ██╗████████╗ █████╗ ██╗
|
|
38
|
+
██╔════╝ ██║╚══██╔══╝ ██╔══██╗██║
|
|
39
|
+
██║ ███╗██║ ██║ ███████║██║
|
|
40
|
+
██║ ██║██║ ██║ ██╔══██║██║
|
|
41
|
+
╚██████╔╝██║ ██║ ██║ ██║██║
|
|
42
|
+
╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝
|
|
43
|
+
`;
|
package/src/index.mjs
CHANGED
|
@@ -1,69 +1,95 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import CommitAction from './actions/CommitAction.mjs';
|
|
3
|
-
import ModelAction from './actions/ModelAction.mjs';
|
|
4
|
-
import TokenAction from './actions/TokenAction.mjs';
|
|
5
|
-
import BaseUrlAction from './actions/BaseUrlAction.mjs';
|
|
6
|
-
import MaxTokenAction from './actions/MaxTokenAction.mjs';
|
|
7
|
-
import { exitProcess } from './utils/Utils.mjs';
|
|
8
|
-
import SelectModelAction from './actions/SelectModelAction.mjs';
|
|
9
|
-
import { BIN, VERSION, DESCRIPTION, NAME, OPENAI_MAX_TOKEN_DEFAULT, asciiArt } from './const.mjs';
|
|
10
|
-
import Logger from './utils/Logger.mjs';
|
|
11
|
-
import chalk from
|
|
12
|
-
|
|
13
|
-
let startTimestamp;
|
|
14
|
-
function registerCommand() {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import CommitAction from './actions/CommitAction.mjs';
|
|
3
|
+
import ModelAction from './actions/ModelAction.mjs';
|
|
4
|
+
import TokenAction from './actions/TokenAction.mjs';
|
|
5
|
+
import BaseUrlAction from './actions/BaseUrlAction.mjs';
|
|
6
|
+
import MaxTokenAction from './actions/MaxTokenAction.mjs';
|
|
7
|
+
import { exitProcess } from './utils/Utils.mjs';
|
|
8
|
+
import SelectModelAction from './actions/SelectModelAction.mjs';
|
|
9
|
+
import { BIN, VERSION, DESCRIPTION, NAME, OPENAI_MAX_TOKEN_DEFAULT, asciiArt } from './const.mjs';
|
|
10
|
+
import Logger from './utils/Logger.mjs';
|
|
11
|
+
import chalk from 'chalk';
|
|
12
|
+
|
|
13
|
+
let startTimestamp;
|
|
14
|
+
function registerCommand() {
|
|
15
|
+
const program = new Command();
|
|
16
|
+
program
|
|
17
|
+
.name(BIN)
|
|
18
|
+
// .description(`${asciiArt}`)
|
|
19
|
+
// .description(`${asciiArt}\n${DESCRIPTION}`)
|
|
20
|
+
.version(VERSION)
|
|
21
|
+
.helpOption(
|
|
22
|
+
'-h',
|
|
23
|
+
chalk.cyan(`显示帮助信息 (baseURL、model、token 分别支持多个,英文逗号隔开)`)
|
|
24
|
+
);
|
|
25
|
+
program
|
|
26
|
+
.command('set-baseURL [baseURL]')
|
|
27
|
+
.description(
|
|
28
|
+
`设置 OpenAI compatible Base URL,请执行 ${chalk.cyan(
|
|
29
|
+
'`' + BIN + ' set-baseURL [baseURL]`'
|
|
30
|
+
)}\n如:https://api.siliconflow.cn/v1`
|
|
31
|
+
)
|
|
32
|
+
.action(BaseUrlAction);
|
|
33
|
+
program
|
|
34
|
+
.command('set-key [key]')
|
|
35
|
+
.description(
|
|
36
|
+
`设置 OpenAI compatible Api Key,请执行 ${chalk.cyan('`' + BIN + ' set-key [key]`')}`
|
|
37
|
+
)
|
|
38
|
+
.action(TokenAction);
|
|
39
|
+
program
|
|
40
|
+
.command('set-model [model]')
|
|
41
|
+
.description(`设置您的模型,请执行 ${chalk.cyan('`' + BIN + ' set-model [model]`')}`)
|
|
42
|
+
.action(ModelAction);
|
|
43
|
+
program
|
|
44
|
+
.command('set-max-token <maxToken>')
|
|
45
|
+
.description(
|
|
46
|
+
`设置您的最大 token 数,默认:${OPENAI_MAX_TOKEN_DEFAULT}(${Math.round(
|
|
47
|
+
OPENAI_MAX_TOKEN_DEFAULT / 1000
|
|
48
|
+
)}k)`
|
|
49
|
+
)
|
|
50
|
+
.action(MaxTokenAction);
|
|
51
|
+
program
|
|
52
|
+
.command('select-model')
|
|
53
|
+
.description(`从模型列表选择,请执行 ${chalk.cyan('`' + BIN + ' select-model`')}`)
|
|
54
|
+
.action(SelectModelAction);
|
|
55
|
+
program
|
|
56
|
+
.option('-d, --dry-run', `等同于 ${chalk.cyan('`git commit --dry-run -m <message>`')}`)
|
|
57
|
+
.option('-e, --allow-empty', `等同于 ${chalk.cyan('`git commit --allow-empty -m <message>`')}`)
|
|
58
|
+
.option('-n, --no-verify', `等同于 ${chalk.cyan('`git commit --no-verify -m <message>`')}`)
|
|
59
|
+
.option(
|
|
60
|
+
'-s, --skip',
|
|
61
|
+
`跳过 ${chalk.cyan('`git add`')} 命令, 只提交已暂存的更改,如:${chalk.cyan(
|
|
62
|
+
'`' + BIN + ' --skip`'
|
|
63
|
+
)}`
|
|
64
|
+
)
|
|
65
|
+
.description(
|
|
66
|
+
`${DESCRIPTION}\n${asciiArt}\n使用 AI 自动生成提交信息并推送
|
|
67
|
+
→ ${chalk.cyan('检查环境')} → ${chalk.cyan('检查目录')} → ${chalk.cyan(
|
|
68
|
+
'检查冲突'
|
|
69
|
+
)} → ${chalk.cyan('处理合并')}\n → ${chalk.cyan('git add')} → ${chalk.cyan(
|
|
70
|
+
'获取diff'
|
|
71
|
+
)} → ${chalk.cyan('AI 生成 commit message')}\n → ${chalk.cyan('git fetch')} → ${chalk.cyan(
|
|
72
|
+
'git merge'
|
|
73
|
+
)} → ${chalk.cyan('检查冲突')} → ${chalk.cyan('git push')}`
|
|
74
|
+
)
|
|
75
|
+
.action(CommitAction);
|
|
76
|
+
program.parse();
|
|
77
|
+
}
|
|
78
|
+
async function prepare() {
|
|
79
|
+
// 第一步检查版本号
|
|
80
|
+
checkPkgVersion();
|
|
81
|
+
}
|
|
82
|
+
function checkPkgVersion() {
|
|
83
|
+
Logger.verbose(`${NAME}@${VERSION}`);
|
|
84
|
+
}
|
|
85
|
+
export default async function core() {
|
|
86
|
+
startTimestamp = Date.now();
|
|
87
|
+
try {
|
|
88
|
+
await prepare();
|
|
89
|
+
// 命令注册
|
|
90
|
+
registerCommand();
|
|
91
|
+
} catch (e) {
|
|
92
|
+
Logger.error(e.message);
|
|
93
|
+
exitProcess(1, startTimestamp);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -1,113 +1,106 @@
|
|
|
1
|
-
import { chat, freeChat } from
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
*
|
|
53
|
-
* @param {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
errorMessage
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
return
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* 获取使用统计信息
|
|
109
|
-
*/
|
|
110
|
-
getUsageMessage() {
|
|
111
|
-
return this.usageMessage;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
1
|
+
import { chat, freeChat } from '../utils/OpenAI.mjs';
|
|
2
|
+
import { generateSystemMessage, formatCompletions } from '../utils/MessageUtils.mjs';
|
|
3
|
+
import { config } from '../utils/Storage.mjs';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
/**
|
|
6
|
+
* AI 服务类
|
|
7
|
+
* 封装所有 AI 相关操作
|
|
8
|
+
*/
|
|
9
|
+
export class AIService {
|
|
10
|
+
constructor(userName) {
|
|
11
|
+
this.userName = userName;
|
|
12
|
+
this.usageMessage = '';
|
|
13
|
+
}
|
|
14
|
+
// 检查是否配置 baseUrl model
|
|
15
|
+
checkConfig() {
|
|
16
|
+
const baseURL = config.get('baseURL');
|
|
17
|
+
const key = config.get('key');
|
|
18
|
+
return baseURL && key;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 生成提交消息
|
|
23
|
+
*/
|
|
24
|
+
async generateCommitMessage(diffString) {
|
|
25
|
+
const request = this.checkConfig() ? chat : freeChat;
|
|
26
|
+
const result = await request({
|
|
27
|
+
messages: [
|
|
28
|
+
{
|
|
29
|
+
role: 'system',
|
|
30
|
+
content: generateSystemMessage(this.userName),
|
|
31
|
+
},
|
|
32
|
+
{ role: 'user', content: diffString },
|
|
33
|
+
],
|
|
34
|
+
});
|
|
35
|
+
if (result.data && result.data.error && !result.data.choices) {
|
|
36
|
+
throw result.data.error;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (result.data.usage) {
|
|
40
|
+
this.usageMessage = chalk.underline(
|
|
41
|
+
`本次模型消耗统计:总数 ${result.data.usage.total_tokens} tokens、输入 ${result.data.usage.prompt_tokens} tokens、输出 ${result.data.usage.completion_tokens} tokens`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return formatCompletions(result);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 让 AI 帮忙分析 git commit 失败原因
|
|
50
|
+
* @param {Object} payload
|
|
51
|
+
* @param {string} payload.errorMessage git commit 抛出的错误
|
|
52
|
+
* @param {string} payload.gitStatus 当前 git status 输出
|
|
53
|
+
* @param {string} payload.hookLogs 如果有 hook 输出,可传入
|
|
54
|
+
*/
|
|
55
|
+
async analyzeCommitFailure({ errorMessage = '', gitStatus = '', hookLogs = '' } = {}) {
|
|
56
|
+
this.usageMessage = '';
|
|
57
|
+
const request = this.checkConfig() ? chat : freeChat;
|
|
58
|
+
const contextParts = [];
|
|
59
|
+
if (errorMessage) {
|
|
60
|
+
contextParts.push(`Git 命令报错:\n${errorMessage}`);
|
|
61
|
+
}
|
|
62
|
+
if (gitStatus) {
|
|
63
|
+
contextParts.push(`git status 输出:\n${gitStatus}`);
|
|
64
|
+
}
|
|
65
|
+
if (hookLogs) {
|
|
66
|
+
contextParts.push(`Hook 输出:\n${hookLogs}`);
|
|
67
|
+
}
|
|
68
|
+
const userPrompt = contextParts.length
|
|
69
|
+
? contextParts.join('\n\n')
|
|
70
|
+
: '未提供额外日志,只知道 git commit 失败。';
|
|
71
|
+
|
|
72
|
+
const result = await request({
|
|
73
|
+
messages: [
|
|
74
|
+
{
|
|
75
|
+
role: 'system',
|
|
76
|
+
content:
|
|
77
|
+
'你是一个资深的 Git 专家,请分析用户提供的 git commit 失败日志,输出造成失败的最可能原因,并给出简洁的排查或修复步骤。请使用中文回答,结构可以是:原因 + 处理建议。',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
role: 'user',
|
|
81
|
+
content: userPrompt,
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
temperature: 0.2,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
if (result.data && result.data.usage) {
|
|
88
|
+
this.usageMessage = chalk.underline(
|
|
89
|
+
`本次模型消耗统计:总数 ${result.data.usage.total_tokens} tokens、输入 ${result.data.usage.prompt_tokens} tokens、输出 ${result.data.usage.completion_tokens} tokens`
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (result.data && result.data.error && !result.data.choices) {
|
|
94
|
+
throw result.data.error;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return formatCompletions(result);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 获取使用统计信息
|
|
102
|
+
*/
|
|
103
|
+
getUsageMessage() {
|
|
104
|
+
return this.usageMessage;
|
|
105
|
+
}
|
|
106
|
+
}
|