@rongchenglu/atc 1.0.6
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/index.js +264 -0
- package/package.json +36 -0
package/index.js
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
#!/usr/bin/env node --no-warnings
|
|
2
|
+
|
|
3
|
+
import yaml from "js-yaml";
|
|
4
|
+
import axios from "axios";
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import readline from "readline";
|
|
8
|
+
import boxen from "boxen";
|
|
9
|
+
import chalk from "chalk";
|
|
10
|
+
|
|
11
|
+
function loadConfig() {
|
|
12
|
+
const configFile = path.join(process.cwd(), "atc-config.yaml");
|
|
13
|
+
|
|
14
|
+
if (!fs.existsSync(configFile)) {
|
|
15
|
+
console.log("错误:找不到atc-config.yaml文件");
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const fileContents = fs.readFileSync(configFile, "utf8");
|
|
21
|
+
const config = yaml.load(fileContents);
|
|
22
|
+
|
|
23
|
+
const requiredKeys = ["base_url", "ds_id", "apikey"];
|
|
24
|
+
for (const key of requiredKeys) {
|
|
25
|
+
if (!(key in config)) {
|
|
26
|
+
console.log(`错误:atc-config.yaml中缺少必需的配置项:${key}`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 处理可选的布尔配置项,设置默认值
|
|
32
|
+
if (config.omit_single_db !== undefined) {
|
|
33
|
+
config.omit_single_db = Boolean(config.omit_single_db);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (config.camel_alias !== undefined) {
|
|
37
|
+
config.camel_alias = Boolean(config.camel_alias);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return config;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.log(`错误:无法解析atc-config.yaml文件:${error.message}`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function fetchBackendVersion(baseUrl) {
|
|
48
|
+
try {
|
|
49
|
+
const response = await axios.get(`${baseUrl}/v1/version`, {
|
|
50
|
+
timeout: 5000,
|
|
51
|
+
});
|
|
52
|
+
return {
|
|
53
|
+
connected: true,
|
|
54
|
+
version: response.data.version || response.data,
|
|
55
|
+
};
|
|
56
|
+
} catch (error) {
|
|
57
|
+
return {
|
|
58
|
+
connected: false,
|
|
59
|
+
error: error.message,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function displayStartupScreen(backendInfo) {
|
|
65
|
+
const statusIcon = backendInfo.connected ? chalk.green("●") : chalk.red("●");
|
|
66
|
+
|
|
67
|
+
const caption = backendInfo.connected
|
|
68
|
+
? `${statusIcon} Backend Version: ${chalk.cyan(
|
|
69
|
+
typeof backendInfo.version === "string"
|
|
70
|
+
? backendInfo.version
|
|
71
|
+
: JSON.stringify(backendInfo.version)
|
|
72
|
+
)}`
|
|
73
|
+
: `${statusIcon} ${chalk.red(
|
|
74
|
+
"Backend connection failed: \n" + backendInfo.error
|
|
75
|
+
)}`;
|
|
76
|
+
|
|
77
|
+
console.log(
|
|
78
|
+
boxen("AskTable Code\n\n" + caption, {
|
|
79
|
+
padding: 1,
|
|
80
|
+
margin: 1,
|
|
81
|
+
borderStyle: "round",
|
|
82
|
+
borderColor: "cyan",
|
|
83
|
+
textAlignment: "center",
|
|
84
|
+
})
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function displayConfigInfo(config) {
|
|
89
|
+
const configLines = [
|
|
90
|
+
chalk.bold.cyan("当前配置信息"),
|
|
91
|
+
"",
|
|
92
|
+
`${chalk.yellow("Base URL:")} ${config.base_url}`,
|
|
93
|
+
`${chalk.yellow("Datasource ID:")} ${config.ds_id}`,
|
|
94
|
+
`${chalk.yellow("API Key:")} ${config.apikey ? config.apikey : "未设置"}`,
|
|
95
|
+
`${chalk.yellow("Omit Single DB:")} ${
|
|
96
|
+
config.omit_single_db !== undefined
|
|
97
|
+
? config.omit_single_db
|
|
98
|
+
? chalk.green("true")
|
|
99
|
+
: chalk.red("false")
|
|
100
|
+
: chalk.gray("未设置")
|
|
101
|
+
}`,
|
|
102
|
+
`${chalk.yellow("Camel Alias:")} ${
|
|
103
|
+
config.camel_alias !== undefined
|
|
104
|
+
? config.camel_alias
|
|
105
|
+
? chalk.green("true")
|
|
106
|
+
: chalk.red("false")
|
|
107
|
+
: chalk.gray("未设置")
|
|
108
|
+
}`,
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
console.log(
|
|
112
|
+
boxen(configLines.join("\n"), {
|
|
113
|
+
padding: 1,
|
|
114
|
+
margin: 1,
|
|
115
|
+
borderStyle: "round",
|
|
116
|
+
borderColor: "green",
|
|
117
|
+
title: "Debug Info",
|
|
118
|
+
titleAlignment: "center",
|
|
119
|
+
})
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function queryToSql(config, question) {
|
|
124
|
+
const url = `${config.base_url}/v1/q2m/`;
|
|
125
|
+
|
|
126
|
+
const headers = {
|
|
127
|
+
Authorization: `Bearer ${config.apikey}`,
|
|
128
|
+
"Content-Type": "application/json",
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const payload = {
|
|
132
|
+
datasource_id: config.ds_id,
|
|
133
|
+
question: question,
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// 添加可选的布尔配置项到请求体中
|
|
137
|
+
if (config.omit_single_db !== undefined) {
|
|
138
|
+
payload.omit_single_db = config.omit_single_db;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (config.camel_alias !== undefined) {
|
|
142
|
+
payload.camel_alias = config.camel_alias;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
const response = await axios.post(url, payload, {
|
|
147
|
+
headers: headers,
|
|
148
|
+
timeout: 60000,
|
|
149
|
+
});
|
|
150
|
+
return {
|
|
151
|
+
success: true,
|
|
152
|
+
data: response.data,
|
|
153
|
+
};
|
|
154
|
+
} catch (error) {
|
|
155
|
+
if (error.response) {
|
|
156
|
+
return {
|
|
157
|
+
success: false,
|
|
158
|
+
error: `HTTP错误:${error.response.status} - ${
|
|
159
|
+
error.response.statusText
|
|
160
|
+
}\n错误详情:${JSON.stringify(error.response.data)}`,
|
|
161
|
+
};
|
|
162
|
+
} else {
|
|
163
|
+
return {
|
|
164
|
+
success: false,
|
|
165
|
+
error: `请求失败:${error.message}`,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function createReadlineInterface() {
|
|
172
|
+
const rl = readline.createInterface({
|
|
173
|
+
input: process.stdin,
|
|
174
|
+
output: process.stdout,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
return rl;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function askQuestion(rl, question) {
|
|
181
|
+
return new Promise((resolve, reject) => {
|
|
182
|
+
if (rl.closed) {
|
|
183
|
+
reject(new Error("readline is closed"));
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
rl.question(question, (answer) => {
|
|
188
|
+
resolve(answer);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function main() {
|
|
194
|
+
// 加载配置
|
|
195
|
+
const config = loadConfig();
|
|
196
|
+
|
|
197
|
+
// 获取后端版本信息
|
|
198
|
+
const backendInfo = await fetchBackendVersion(config.base_url);
|
|
199
|
+
|
|
200
|
+
// 打印启动页
|
|
201
|
+
displayStartupScreen(backendInfo);
|
|
202
|
+
|
|
203
|
+
// 如果后端连接失败,直接退出
|
|
204
|
+
if (!backendInfo.connected) {
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const rl = createReadlineInterface();
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
while (true) {
|
|
212
|
+
const question = (
|
|
213
|
+
await askQuestion(rl, "请输入自然语言的查询语句:")
|
|
214
|
+
).trim();
|
|
215
|
+
|
|
216
|
+
if (question.toLowerCase() === "exit") {
|
|
217
|
+
console.log("再见!");
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (question.toLowerCase() === "/info") {
|
|
222
|
+
displayConfigInfo(config);
|
|
223
|
+
console.log("\n" + "=".repeat(50) + "\n");
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (!question) {
|
|
228
|
+
console.log("请输入有效的查询语句\n");
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
console.log("正在转换SQL...");
|
|
233
|
+
const result = await queryToSql(config, question);
|
|
234
|
+
|
|
235
|
+
if (result.success) {
|
|
236
|
+
console.log(chalk.green("✓ 转换成功"));
|
|
237
|
+
console.log(result.data);
|
|
238
|
+
} else {
|
|
239
|
+
console.log(chalk.red("✗ 转换失败"));
|
|
240
|
+
console.log(chalk.red(result.error));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
console.log("\n" + "=".repeat(50) + "\n");
|
|
244
|
+
}
|
|
245
|
+
} catch (error) {
|
|
246
|
+
if (error.code === "SIGINT") {
|
|
247
|
+
} else {
|
|
248
|
+
console.log(`发生错误:${error.message}\n`);
|
|
249
|
+
}
|
|
250
|
+
} finally {
|
|
251
|
+
rl.close();
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// 处理 Ctrl+C 中断
|
|
256
|
+
process.on("SIGINT", () => {
|
|
257
|
+
console.log("\n\n再见!");
|
|
258
|
+
process.exit(0);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
main().catch((error) => {
|
|
262
|
+
console.log(`发生错误:${error.message}`);
|
|
263
|
+
process.exit(1);
|
|
264
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rongchenglu/atc",
|
|
3
|
+
"version": "1.0.6",
|
|
4
|
+
"description": "asktable 自然语言转SQL 工具",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"axios": "^1.11.0",
|
|
9
|
+
"boxen": "^8.0.1",
|
|
10
|
+
"chalk": "^5.6.2",
|
|
11
|
+
"js-yaml": "^4.1.0"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/DataMini/gen_mybatis.git"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"mybatis",
|
|
22
|
+
"nl2sql",
|
|
23
|
+
"asktable",
|
|
24
|
+
"datamini",
|
|
25
|
+
"atc"
|
|
26
|
+
],
|
|
27
|
+
"author": "datamini",
|
|
28
|
+
"license": "ISC",
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/DataMini/gen_mybatis/issues"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/DataMini/gen_mybatis#readme",
|
|
33
|
+
"bin": {
|
|
34
|
+
"atc": "./index.js"
|
|
35
|
+
}
|
|
36
|
+
}
|