i18n-sync-mcp-server 1.0.0 → 1.0.1
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 +96 -13
- package/i18n-sync-server.cjs +226 -33
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,17 +7,18 @@
|
|
|
7
7
|
- 将本地 i18n 映射上传到远程语言包服务
|
|
8
8
|
- 支持批量同步映射
|
|
9
9
|
- 自动处理认证和项目配置
|
|
10
|
+
- 对接 Hwork-Workbench 工作台多语言接口
|
|
10
11
|
|
|
11
12
|
## 安装
|
|
12
13
|
|
|
13
14
|
```bash
|
|
14
|
-
npm install -g
|
|
15
|
+
npm install -g i18n-sync-mcp-server
|
|
15
16
|
```
|
|
16
17
|
|
|
17
18
|
或使用 npx:
|
|
18
19
|
|
|
19
20
|
```bash
|
|
20
|
-
npx
|
|
21
|
+
npx i18n-sync-mcp-server
|
|
21
22
|
```
|
|
22
23
|
|
|
23
24
|
## 配置
|
|
@@ -29,12 +30,15 @@ npx @your-org/i18n-sync-mcp-server
|
|
|
29
30
|
"mcpServers": {
|
|
30
31
|
"i18n-sync": {
|
|
31
32
|
"command": "npx",
|
|
32
|
-
"args": ["-y", "
|
|
33
|
+
"args": ["-y", "i18n-sync-mcp-server"],
|
|
33
34
|
"env": {
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"I18N_MODULE_CODE": "
|
|
37
|
-
"
|
|
35
|
+
"I18N_API_KEY": "your-api-key",
|
|
36
|
+
"I18N_APP_CODE": "SP000610",
|
|
37
|
+
"I18N_MODULE_CODE": "M2024080516210622558046",
|
|
38
|
+
"I18N_GROUP_TYPE": "2",
|
|
39
|
+
"I18N_OPERATOR": "your-user-id",
|
|
40
|
+
"I18N_KEY_PREFIX": "ML_chain_app_",
|
|
41
|
+
"I18N_REMOTE_API_URL": "https://gw-qd-aliyun-stage.haier.net/gzt/hwork-backend-workbench/v1/plat/application/i18n/costom/batchSaveOrUpdate"
|
|
38
42
|
}
|
|
39
43
|
}
|
|
40
44
|
}
|
|
@@ -45,25 +49,33 @@ npx @your-org/i18n-sync-mcp-server
|
|
|
45
49
|
|
|
46
50
|
| 变量名 | 必需 | 说明 |
|
|
47
51
|
|--------|------|------|
|
|
48
|
-
| `
|
|
49
|
-
| `
|
|
50
|
-
| `I18N_MODULE_CODE` |
|
|
51
|
-
| `
|
|
52
|
+
| `I18N_API_KEY` | 是 | Authorization ApiKey(用于调用远程语言包接口) |
|
|
53
|
+
| `I18N_APP_CODE` | 是 | 应用编码,对应 applicationCode |
|
|
54
|
+
| `I18N_MODULE_CODE` | 是 | 模块编码,对应 businessCode |
|
|
55
|
+
| `I18N_GROUP_TYPE` | 是 | 分组类型:1-子产品;2-应用;3-组件;4-菜单 |
|
|
56
|
+
| `I18N_OPERATOR` | 是 | 操作人标识,用于 X-USER 请求头 |
|
|
57
|
+
| `I18N_KEY_PREFIX` | 是 | 编码前缀,如 `ML_chain_app_`,远程会自动拼接 |
|
|
58
|
+
| `I18N_REMOTE_API_URL` | 是 | 远程 API 地址 |
|
|
52
59
|
|
|
53
60
|
## 可用工具
|
|
54
61
|
|
|
55
62
|
### sync_mapping_to_remote
|
|
56
63
|
|
|
57
|
-
|
|
64
|
+
将本地映射和多语言翻译同步到远程语言包服务。
|
|
65
|
+
|
|
66
|
+
**重要**:上传时至少需要包含英文翻译(en-US)。
|
|
58
67
|
|
|
59
68
|
**参数:**
|
|
60
69
|
|
|
61
70
|
- `mappings` (array, 必需): 映射数组,每项包含:
|
|
62
71
|
- `text` (string): 中文文案
|
|
63
72
|
- `key` (string): ML_ key
|
|
73
|
+
- `translations` (object, 可选): 多语言翻译对象
|
|
74
|
+
- key: 语言代码(如 en-US, th-TH)
|
|
75
|
+
- value: 翻译文本
|
|
64
76
|
- `localeFilePath` (string, 可选): zh-CN.ts 文件路径
|
|
65
77
|
|
|
66
|
-
|
|
78
|
+
**示例 1:仅上传中文映射**
|
|
67
79
|
|
|
68
80
|
```javascript
|
|
69
81
|
{
|
|
@@ -74,6 +86,77 @@ npx @your-org/i18n-sync-mcp-server
|
|
|
74
86
|
}
|
|
75
87
|
```
|
|
76
88
|
|
|
89
|
+
返回结果中的 mappings 可用于更新 zh-CN.ts(格式为 `ML_key: 中文`):
|
|
90
|
+
```typescript
|
|
91
|
+
// zh-CN.ts
|
|
92
|
+
export default {
|
|
93
|
+
'ML_chain_app_3APV9EU0HUVC9OX': '新增',
|
|
94
|
+
'ML_chain_app_3APODM6S81RFP9W': '编辑',
|
|
95
|
+
} as Record<string, string>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**示例 2:上传中文映射和多语言翻译**
|
|
99
|
+
|
|
100
|
+
```javascript
|
|
101
|
+
{
|
|
102
|
+
"mappings": [
|
|
103
|
+
{
|
|
104
|
+
"text": "新增",
|
|
105
|
+
"key": "ML_chain_app_3APV9EU0HUVC9OX",
|
|
106
|
+
"translations": {
|
|
107
|
+
"en-US": "Add",
|
|
108
|
+
"th-TH": "เพิ่ม",
|
|
109
|
+
"ms-MY": "Tambah"
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"text": "编辑",
|
|
114
|
+
"key": "ML_chain_app_3APODM6S81RFP9W",
|
|
115
|
+
"translations": {
|
|
116
|
+
"en-US": "Edit",
|
|
117
|
+
"th-TH": "แก้ไข",
|
|
118
|
+
"ms-MY": "Edit"
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## 接口说明
|
|
126
|
+
|
|
127
|
+
本服务器对接 Hwork-Workbench 工作台的多语言接口:
|
|
128
|
+
|
|
129
|
+
**接口地址:** `/v1/plat/application/i18n/costom/batchSaveOrUpdate`
|
|
130
|
+
|
|
131
|
+
**请求格式:**
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"applicationCode": "所属子产品编码",
|
|
136
|
+
"dataList": [
|
|
137
|
+
{
|
|
138
|
+
"businessCode": "所属分组编码",
|
|
139
|
+
"customCode": "ML_xxx (自定义编码)",
|
|
140
|
+
"customName": "中文名称",
|
|
141
|
+
"groupType": "分组类型",
|
|
142
|
+
"i18NTextVOList": [
|
|
143
|
+
{
|
|
144
|
+
"languageCode": "zh-CN",
|
|
145
|
+
"resourceText": "中文翻译"
|
|
146
|
+
}
|
|
147
|
+
]
|
|
148
|
+
}
|
|
149
|
+
],
|
|
150
|
+
"operator": "操作人"
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**请求头:**
|
|
155
|
+
|
|
156
|
+
- `Authorization`: 认证 Token
|
|
157
|
+
- `X-USER`: 操作人标识
|
|
158
|
+
- `Content-Type`: application/json
|
|
159
|
+
|
|
77
160
|
## 许可证
|
|
78
161
|
|
|
79
162
|
MIT
|
package/i18n-sync-server.cjs
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* 国际化映射同步 MCP Server
|
|
4
4
|
* 用于将本地映射上传到远程语言包服务
|
|
5
|
+
*
|
|
6
|
+
* 对接接口:/v1/plat/application/i18n/costom/batchSaveOrUpdate
|
|
5
7
|
*/
|
|
6
8
|
|
|
7
9
|
const http = require('http');
|
|
@@ -11,35 +13,72 @@ const path = require('path');
|
|
|
11
13
|
|
|
12
14
|
// ============= 配置区域(通过环境变量配置) =============
|
|
13
15
|
const CONFIG = {
|
|
14
|
-
//
|
|
15
|
-
remoteApiUrl: process.env.I18N_REMOTE_API_URL || '
|
|
16
|
-
// 认证
|
|
17
|
-
authToken: process.env.
|
|
18
|
-
// 项目标识
|
|
19
|
-
|
|
20
|
-
//
|
|
16
|
+
// 远程接口基础地址
|
|
17
|
+
remoteApiUrl: process.env.I18N_REMOTE_API_URL || '',
|
|
18
|
+
// 认证 ApiKey (Authorization header)
|
|
19
|
+
authToken: process.env.I18N_API_KEY || '',
|
|
20
|
+
// 项目标识 (applicationCode)
|
|
21
|
+
appCode: process.env.I18N_APP_CODE || '',
|
|
22
|
+
// 分组标识 (businessCode)
|
|
21
23
|
moduleCode: process.env.I18N_MODULE_CODE || '',
|
|
24
|
+
// 分组类型 (groupType)
|
|
25
|
+
groupType: process.env.I18N_GROUP_TYPE || '',
|
|
26
|
+
// 操作人 (X-USER header)
|
|
27
|
+
operator: process.env.I18N_OPERATOR || '',
|
|
28
|
+
// 编码前缀(远程会自动拼接,请求时需去除)
|
|
29
|
+
keyPrefix: process.env.I18N_KEY_PREFIX || '',
|
|
22
30
|
};
|
|
23
31
|
// ===================================================
|
|
24
32
|
|
|
33
|
+
/**
|
|
34
|
+
* 去除 key 的前缀
|
|
35
|
+
* @param {string} key - 原始 key(可能带前缀)
|
|
36
|
+
* @returns {string} - 去除前缀后的 key
|
|
37
|
+
*/
|
|
38
|
+
function removeKeyPrefix(key) {
|
|
39
|
+
if (!CONFIG.keyPrefix) return key;
|
|
40
|
+
if (key.startsWith(CONFIG.keyPrefix)) {
|
|
41
|
+
return key.slice(CONFIG.keyPrefix.length);
|
|
42
|
+
}
|
|
43
|
+
return key;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 添加 key 的前缀
|
|
48
|
+
* @param {string} key - 原始 key(不带前缀)
|
|
49
|
+
* @returns {string} - 添加前缀后的完整 key
|
|
50
|
+
*/
|
|
51
|
+
function addKeyPrefix(key) {
|
|
52
|
+
if (!CONFIG.keyPrefix) return key;
|
|
53
|
+
if (key.startsWith(CONFIG.keyPrefix)) {
|
|
54
|
+
return key; // 已有前缀,不重复添加
|
|
55
|
+
}
|
|
56
|
+
return CONFIG.keyPrefix + key;
|
|
57
|
+
}
|
|
58
|
+
|
|
25
59
|
// MCP 协议实现
|
|
26
60
|
class MCPServer {
|
|
27
61
|
constructor() {
|
|
28
62
|
this.tools = {
|
|
29
63
|
'sync_mapping_to_remote': {
|
|
30
64
|
name: 'sync_mapping_to_remote',
|
|
31
|
-
description: '
|
|
65
|
+
description: '将本地映射同步到远程语言包服务,支持多语言翻译',
|
|
32
66
|
inputSchema: {
|
|
33
67
|
type: 'object',
|
|
34
68
|
properties: {
|
|
35
69
|
mappings: {
|
|
36
70
|
type: 'array',
|
|
37
|
-
description: '要上传的映射数组,每项包含 text
|
|
71
|
+
description: '要上传的映射数组,每项包含 text(中文文案)、key(ML_ key)和可选的 translations(多语言翻译)',
|
|
38
72
|
items: {
|
|
39
73
|
type: 'object',
|
|
40
74
|
properties: {
|
|
41
75
|
text: { type: 'string', description: '中文文案' },
|
|
42
|
-
key: { type: 'string', description: 'ML_ key' }
|
|
76
|
+
key: { type: 'string', description: 'ML_ key' },
|
|
77
|
+
translations: {
|
|
78
|
+
type: 'object',
|
|
79
|
+
description: '可选的多语言翻译对象,key 为语言代码(如 en-US, th-TH),value 为翻译文本',
|
|
80
|
+
additionalProperties: { type: 'string' }
|
|
81
|
+
}
|
|
43
82
|
},
|
|
44
83
|
required: ['text', 'key']
|
|
45
84
|
}
|
|
@@ -108,43 +147,165 @@ class MCPServer {
|
|
|
108
147
|
type: 'text',
|
|
109
148
|
text: JSON.stringify({
|
|
110
149
|
success: false,
|
|
111
|
-
message: '请配置
|
|
150
|
+
message: '请配置 I18N_API_KEY 环境变量',
|
|
112
151
|
hint: '在项目根目录的 .env 文件或系统环境变量中设置'
|
|
113
152
|
}, null, 2)
|
|
114
153
|
}]
|
|
115
154
|
};
|
|
116
155
|
}
|
|
117
156
|
|
|
157
|
+
if (!CONFIG.appCode) {
|
|
158
|
+
return {
|
|
159
|
+
content: [{
|
|
160
|
+
type: 'text',
|
|
161
|
+
text: JSON.stringify({
|
|
162
|
+
success: false,
|
|
163
|
+
message: '请配置 I18N_APP_CODE 环境变量',
|
|
164
|
+
hint: '这是 applicationCode,用于标识所属子产品'
|
|
165
|
+
}, null, 2)
|
|
166
|
+
}]
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (!CONFIG.keyPrefix) {
|
|
171
|
+
return {
|
|
172
|
+
content: [{
|
|
173
|
+
type: 'text',
|
|
174
|
+
text: JSON.stringify({
|
|
175
|
+
success: false,
|
|
176
|
+
message: '请配置 I18N_KEY_PREFIX 环境变量',
|
|
177
|
+
hint: '这是编码前缀,如 ML_chain_app_,远程会自动拼接'
|
|
178
|
+
}, null, 2)
|
|
179
|
+
}]
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// 构建符合 OpenAPI 规范的请求体
|
|
184
|
+
// I18NCustomBatchSaveParam 格式
|
|
185
|
+
const dataList = mappings.map(m => {
|
|
186
|
+
// 去除前缀后的编码(用于接口请求)
|
|
187
|
+
const codeWithoutPrefix = removeKeyPrefix(m.key);
|
|
188
|
+
// 带前缀的完整编码(用于 zh-CN.ts)
|
|
189
|
+
const codeWithPrefix = addKeyPrefix(codeWithoutPrefix);
|
|
190
|
+
|
|
191
|
+
// 基础的中文翻译
|
|
192
|
+
const i18NTextVOList = [
|
|
193
|
+
{
|
|
194
|
+
languageCode: 'zh-CN',
|
|
195
|
+
resourceText: m.text
|
|
196
|
+
}
|
|
197
|
+
];
|
|
198
|
+
|
|
199
|
+
// 如果提供了多语言翻译,添加到列表中
|
|
200
|
+
if (m.translations && typeof m.translations === 'object') {
|
|
201
|
+
for (const [langCode, translation] of Object.entries(m.translations)) {
|
|
202
|
+
if (translation && typeof translation === 'string') {
|
|
203
|
+
i18NTextVOList.push({
|
|
204
|
+
languageCode: langCode,
|
|
205
|
+
resourceText: translation
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
// 所属分组编码
|
|
213
|
+
businessCode: CONFIG.moduleCode,
|
|
214
|
+
// 自定义编码(去除前缀后的编码,远程会自动拼接前缀)
|
|
215
|
+
customCode: codeWithoutPrefix,
|
|
216
|
+
// 名称(中文)
|
|
217
|
+
customName: m.text,
|
|
218
|
+
// 分组类型
|
|
219
|
+
groupType: CONFIG.groupType || '2',
|
|
220
|
+
// 多语言翻译列表
|
|
221
|
+
i18NTextVOList: i18NTextVOList,
|
|
222
|
+
// 保存完整编码供返回使用
|
|
223
|
+
_fullKey: codeWithPrefix
|
|
224
|
+
};
|
|
225
|
+
});
|
|
226
|
+
|
|
118
227
|
const requestBody = {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
key: m.key,
|
|
123
|
-
zhCN: m.text,
|
|
124
|
-
}))
|
|
228
|
+
applicationCode: CONFIG.appCode,
|
|
229
|
+
dataList: dataList,
|
|
230
|
+
operator: CONFIG.operator || 'i18n-sync-server'
|
|
125
231
|
};
|
|
126
232
|
|
|
233
|
+
const headers = {
|
|
234
|
+
'Content-Type': 'application/json',
|
|
235
|
+
'Authorization': CONFIG.authToken,
|
|
236
|
+
'X-USER': CONFIG.operator || 'i18n-sync-server'
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// 生成 curl 命令
|
|
240
|
+
const curlCommand = this.generateCurlCommand(CONFIG.remoteApiUrl, headers, requestBody);
|
|
241
|
+
|
|
242
|
+
// 输出请求信息到 stderr(便于调试)
|
|
243
|
+
process.stderr.write('\n========== 请求信息 ==========\n');
|
|
244
|
+
process.stderr.write(`映射数量: ${mappings.length}\n`);
|
|
245
|
+
process.stderr.write(`包含翻译: ${mappings.some(m => m.translations) ? '是' : '否'}\n`);
|
|
246
|
+
if (mappings.some(m => m.translations)) {
|
|
247
|
+
const languages = new Set();
|
|
248
|
+
mappings.forEach(m => {
|
|
249
|
+
if (m.translations) {
|
|
250
|
+
Object.keys(m.translations).forEach(lang => languages.add(lang));
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
process.stderr.write(`翻译语言: ${Array.from(languages).join(', ')}\n`);
|
|
254
|
+
}
|
|
255
|
+
process.stderr.write('\n=== cURL 命令 ===\n');
|
|
256
|
+
process.stderr.write(curlCommand + '\n');
|
|
257
|
+
process.stderr.write('============================\n\n');
|
|
258
|
+
|
|
127
259
|
const response = await this.httpRequest({
|
|
128
260
|
url: CONFIG.remoteApiUrl,
|
|
129
261
|
method: 'POST',
|
|
130
|
-
headers:
|
|
131
|
-
'Content-Type': 'application/json',
|
|
132
|
-
'Authorization': `Bearer ${CONFIG.authToken}`
|
|
133
|
-
},
|
|
262
|
+
headers: headers,
|
|
134
263
|
body: JSON.stringify(requestBody)
|
|
135
264
|
});
|
|
136
265
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
266
|
+
// 解析响应 Result«ExcelResultVO»
|
|
267
|
+
if (response.code === 200 || response.code === 0) {
|
|
268
|
+
const data = response.data || {};
|
|
269
|
+
const hasErrors = data.excelParseResultVOList && data.excelParseResultVOList.length > 0;
|
|
270
|
+
|
|
271
|
+
// 构建返回的映射信息(包含完整的 key,用于更新 zh-CN.ts)
|
|
272
|
+
const resultMappings = dataList.map((item, index) => ({
|
|
273
|
+
text: mappings[index].text,
|
|
274
|
+
key: item._fullKey, // 带前缀的完整 key
|
|
275
|
+
requestKey: item.customCode // 请求时使用的 key(不带前缀)
|
|
276
|
+
}));
|
|
277
|
+
|
|
278
|
+
return {
|
|
279
|
+
content: [{
|
|
280
|
+
type: 'text',
|
|
281
|
+
text: JSON.stringify({
|
|
282
|
+
success: true,
|
|
283
|
+
message: `成功同步 ${mappings.length} 个映射到远程服务`,
|
|
284
|
+
uploadedCount: data.successNum || mappings.length,
|
|
285
|
+
serverMessage: data.resMessage || '同步完成',
|
|
286
|
+
hasErrors: hasErrors,
|
|
287
|
+
errors: data.excelParseResultVOList || [],
|
|
288
|
+
keyPrefix: CONFIG.keyPrefix,
|
|
289
|
+
mappings: resultMappings,
|
|
290
|
+
curlCommand: curlCommand
|
|
291
|
+
}, null, 2)
|
|
292
|
+
}]
|
|
293
|
+
};
|
|
294
|
+
} else {
|
|
295
|
+
return {
|
|
296
|
+
content: [{
|
|
297
|
+
type: 'text',
|
|
298
|
+
text: JSON.stringify({
|
|
299
|
+
success: false,
|
|
300
|
+
message: response.message || '同步失败',
|
|
301
|
+
code: response.code,
|
|
302
|
+
response: response,
|
|
303
|
+
curlCommand: curlCommand,
|
|
304
|
+
hint: '请检查环境变量配置是否正确,或使用 curl 命令手动测试接口'
|
|
305
|
+
}, null, 2)
|
|
306
|
+
}]
|
|
307
|
+
};
|
|
308
|
+
}
|
|
148
309
|
} catch (error) {
|
|
149
310
|
// 如果是模拟模式(接口不存在),返回模拟成功
|
|
150
311
|
if (error.message.includes('ENOTFOUND') || error.message.includes('ECONNREFUSED')) {
|
|
@@ -161,8 +322,40 @@ class MCPServer {
|
|
|
161
322
|
}]
|
|
162
323
|
};
|
|
163
324
|
}
|
|
164
|
-
|
|
325
|
+
|
|
326
|
+
// 其他错误
|
|
327
|
+
return {
|
|
328
|
+
content: [{
|
|
329
|
+
type: 'text',
|
|
330
|
+
text: JSON.stringify({
|
|
331
|
+
success: false,
|
|
332
|
+
message: '请求失败: ' + error.message,
|
|
333
|
+
error: error.toString(),
|
|
334
|
+
hint: '请检查网络连接和接口地址是否正确'
|
|
335
|
+
}, null, 2)
|
|
336
|
+
}]
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// 生成 curl 命令
|
|
342
|
+
generateCurlCommand(url, headers, body) {
|
|
343
|
+
let curl = `curl -X POST "${url}"`;
|
|
344
|
+
|
|
345
|
+
// 添加请求头
|
|
346
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
347
|
+
curl += ` \\\n -H "${key}: ${value}"`;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// 添加请求体
|
|
351
|
+
if (body) {
|
|
352
|
+
const bodyStr = typeof body === 'string' ? body : JSON.stringify(body);
|
|
353
|
+
// 转义单引号和反斜杠
|
|
354
|
+
const escapedBody = bodyStr.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
|
355
|
+
curl += ` \\\n -d '${escapedBody}'`;
|
|
165
356
|
}
|
|
357
|
+
|
|
358
|
+
return curl;
|
|
166
359
|
}
|
|
167
360
|
|
|
168
361
|
// HTTP 请求封装
|