cyymall-cli 0.1.0
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 +91 -0
- package/bin/cyy.js +2 -0
- package/package.json +33 -0
- package/src/biz.js +14 -0
- package/src/cli.js +208 -0
- package/src/commands/apiCall.js +149 -0
- package/src/commands/auth.js +134 -0
- package/src/commands/cart.js +55 -0
- package/src/commands/order.js +327 -0
- package/src/commands/product.js +56 -0
- package/src/commands/serve.js +84 -0
- package/src/commands/shop.js +287 -0
- package/src/config.js +82 -0
- package/src/http.js +94 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const crypto = require("crypto");
|
|
5
|
+
const http = require("../http");
|
|
6
|
+
const config = require("../config");
|
|
7
|
+
const biz = require("../biz");
|
|
8
|
+
|
|
9
|
+
function envelope(success, message, data, exitCode) {
|
|
10
|
+
const traceId = `local-${crypto.randomBytes(8).toString("hex")}`;
|
|
11
|
+
console.log(
|
|
12
|
+
JSON.stringify(
|
|
13
|
+
{
|
|
14
|
+
success,
|
|
15
|
+
code: success ? "OK" : "UPSTREAM_ERROR",
|
|
16
|
+
message,
|
|
17
|
+
data,
|
|
18
|
+
traceId,
|
|
19
|
+
},
|
|
20
|
+
null,
|
|
21
|
+
2,
|
|
22
|
+
),
|
|
23
|
+
);
|
|
24
|
+
process.exit(exitCode ?? (success ? 0 : 2));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {{ bodyFile?: string, bodyJson?: string }} opts
|
|
29
|
+
*/
|
|
30
|
+
async function preSettle(opts) {
|
|
31
|
+
const cfg = config.loadConfig();
|
|
32
|
+
if (!cfg?.token) {
|
|
33
|
+
console.error("cyy: not logged in.");
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
let raw = opts.bodyJson;
|
|
37
|
+
if (opts.bodyFile) raw = fs.readFileSync(opts.bodyFile, "utf8");
|
|
38
|
+
if (!raw) {
|
|
39
|
+
console.error("cyy: provide --body-file or --body-json");
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
const url = http.moduleUrl("ORDER", "/app/order/preSettleOrder");
|
|
43
|
+
const headers = /** @type {Record<string,string>} */ (http.buildAuthHeaders(cfg));
|
|
44
|
+
const { ok, json } = await http.request(url, {
|
|
45
|
+
method: "POST",
|
|
46
|
+
headers,
|
|
47
|
+
body: raw,
|
|
48
|
+
});
|
|
49
|
+
const success = ok && biz.isBizSuccess(json);
|
|
50
|
+
envelope(success, success ? "success" : "pre-settle failed", { upstream: json }, success ? 0 : 2);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @param {{ bodyFile?: string, bodyJson?: string }} opts
|
|
55
|
+
*/
|
|
56
|
+
async function confirm(opts) {
|
|
57
|
+
const cfg = config.loadConfig();
|
|
58
|
+
if (!cfg?.token) {
|
|
59
|
+
console.error("cyy: not logged in.");
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
let raw = opts.bodyJson;
|
|
63
|
+
if (opts.bodyFile) raw = fs.readFileSync(opts.bodyFile, "utf8");
|
|
64
|
+
if (!raw) {
|
|
65
|
+
console.error("cyy: provide --body-file or --body-json");
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
const url = http.moduleUrl("ORDER", "/app/order/confirmOrder");
|
|
69
|
+
const headers = /** @type {Record<string,string>} */ (http.buildAuthHeaders(cfg));
|
|
70
|
+
const { ok, json } = await http.request(url, {
|
|
71
|
+
method: "POST",
|
|
72
|
+
headers,
|
|
73
|
+
body: raw,
|
|
74
|
+
});
|
|
75
|
+
const success = ok && biz.isBizSuccess(json);
|
|
76
|
+
envelope(success, success ? "success" : "confirm failed", { upstream: json }, success ? 0 : 2);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function payUrl(opts) {
|
|
80
|
+
const cfg = config.loadConfig();
|
|
81
|
+
if (!cfg?.token) {
|
|
82
|
+
console.error("cyy: not logged in.");
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
const orderId = opts.orderId;
|
|
86
|
+
if (!orderId) {
|
|
87
|
+
console.error("cyy: --order-id required");
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const url = http.moduleUrl("DEFAULT", "/member/getInfo/V2");
|
|
92
|
+
const headers = /** @type {Record<string,string>} */ (http.buildAuthHeaders(cfg));
|
|
93
|
+
delete headers["Content-Type"];
|
|
94
|
+
|
|
95
|
+
const { ok, json } = await http.request(url, { method: "GET", headers, body: null });
|
|
96
|
+
|
|
97
|
+
let phone = String(cfg.phone || "");
|
|
98
|
+
let nick = "";
|
|
99
|
+
let img = "";
|
|
100
|
+
if (ok && biz.isBizSuccess(json) && json && typeof json === "object") {
|
|
101
|
+
const d = /** @type {{data?:Record<string,unknown>}} */ (json).data;
|
|
102
|
+
if (d) {
|
|
103
|
+
phone = String(d.memberPhone || phone);
|
|
104
|
+
nick = String(d.memberNick || "");
|
|
105
|
+
img = String(d.headerImg || "");
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const siteId = cfg.site_id;
|
|
110
|
+
const shopId = cfg.shop_id;
|
|
111
|
+
const enc = encodeURIComponent;
|
|
112
|
+
const payUrlStr = `https://dhcmall.ifoodbuy.com/H5/#/replacePay?phone=${enc(phone)}&orderid=${enc(String(orderId))}&img=${enc(img)}&nick=${enc(nick)}&siteId=${siteId}&shopId=${shopId}&share=true`;
|
|
113
|
+
|
|
114
|
+
envelope(true, "success", { payUrl: payUrlStr, orderId }, 0);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @param {object} opts
|
|
119
|
+
*/
|
|
120
|
+
async function quick(opts) {
|
|
121
|
+
const cfg = config.loadConfig();
|
|
122
|
+
if (!cfg?.token) {
|
|
123
|
+
console.error("cyy: not logged in.");
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const keyword = opts.keyword;
|
|
128
|
+
const quantity = Number(opts.quantity || 1);
|
|
129
|
+
const unit = opts.unit || "袋";
|
|
130
|
+
const shopId = Number(opts.shopId ?? cfg.shop_id);
|
|
131
|
+
const siteId = Number(opts.siteId ?? cfg.site_id);
|
|
132
|
+
|
|
133
|
+
const headers = /** @type {Record<string,string>} */ (http.buildAuthHeaders(cfg));
|
|
134
|
+
|
|
135
|
+
// 1) Search
|
|
136
|
+
const searchUrl = http.moduleUrl("PRODUCT", "/app/product/getSkuList");
|
|
137
|
+
const searchBody = JSON.stringify({
|
|
138
|
+
pageNum: 1,
|
|
139
|
+
pageSize: 10,
|
|
140
|
+
shopId,
|
|
141
|
+
siteId,
|
|
142
|
+
spuName: keyword,
|
|
143
|
+
stockFlag: "0",
|
|
144
|
+
});
|
|
145
|
+
let r = await http.request(searchUrl, {
|
|
146
|
+
method: "POST",
|
|
147
|
+
headers,
|
|
148
|
+
body: searchBody,
|
|
149
|
+
});
|
|
150
|
+
if (!r.ok || !biz.isBizSuccess(r.json)) {
|
|
151
|
+
envelope(false, "search failed", { upstream: r.json }, 2);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const list =
|
|
155
|
+
r.json &&
|
|
156
|
+
typeof r.json === "object" &&
|
|
157
|
+
r.json.data &&
|
|
158
|
+
typeof r.json.data === "object"
|
|
159
|
+
? /** @type {{list?:unknown[]}} */ (r.json.data).list
|
|
160
|
+
: [];
|
|
161
|
+
const products = Array.isArray(list) ? list : [];
|
|
162
|
+
if (!products.length) {
|
|
163
|
+
envelope(false, "no products found", {}, 2);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const product = /** @type {Record<string,unknown>} */ (products[0]);
|
|
168
|
+
const skuId = product.id;
|
|
169
|
+
const skuCode = product.skuCode;
|
|
170
|
+
const skuName = product.spuName;
|
|
171
|
+
const stockQty = product.stockAllQuantity ?? 0;
|
|
172
|
+
const promotionList = Array.isArray(product.promotionList) ? product.promotionList : [];
|
|
173
|
+
|
|
174
|
+
let price = 0;
|
|
175
|
+
let unitRate = 1;
|
|
176
|
+
for (const p of promotionList) {
|
|
177
|
+
const row = /** @type {Record<string,unknown>} */ (p);
|
|
178
|
+
if (row.skuSaleUnitName === unit) {
|
|
179
|
+
price = Number(row.salePrice || 0);
|
|
180
|
+
unitRate = Number(row.unitRate ?? 1);
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (!price) {
|
|
185
|
+
envelope(
|
|
186
|
+
false,
|
|
187
|
+
`unit "${unit}" not found in promotionList`,
|
|
188
|
+
{ availableUnits: promotionList.map((x) => /** @type {{skuSaleUnitName?:string}} */ (x).skuSaleUnitName) },
|
|
189
|
+
2,
|
|
190
|
+
);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 2) Cart
|
|
195
|
+
const cartBody = JSON.stringify({
|
|
196
|
+
cartItemList: [
|
|
197
|
+
{
|
|
198
|
+
skuCode,
|
|
199
|
+
skuName,
|
|
200
|
+
skuQty: quantity,
|
|
201
|
+
skuSaleUnit: unit,
|
|
202
|
+
listPrice: price,
|
|
203
|
+
salePrice: price,
|
|
204
|
+
unitRate,
|
|
205
|
+
selectFlag: true,
|
|
206
|
+
},
|
|
207
|
+
],
|
|
208
|
+
cartModel: "append",
|
|
209
|
+
shopId,
|
|
210
|
+
siteId,
|
|
211
|
+
});
|
|
212
|
+
const cartUrl = http.moduleUrl("ORDER", "/app/order/cart");
|
|
213
|
+
r = await http.request(cartUrl, { method: "POST", headers, body: cartBody });
|
|
214
|
+
if (!r.ok || !biz.isBizSuccess(r.json)) {
|
|
215
|
+
envelope(false, "cart failed", { upstream: r.json }, 2);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const cartData =
|
|
219
|
+
r.json && typeof r.json === "object" && "data" in r.json
|
|
220
|
+
? /** @type {{data?:{cartItemList?:{addTime?:string}[]}}} */ (r.json).data
|
|
221
|
+
: undefined;
|
|
222
|
+
const addTime = cartData?.cartItemList?.[0]?.addTime;
|
|
223
|
+
if (!addTime) {
|
|
224
|
+
envelope(false, "missing addTime from cart response", { upstream: r.json }, 2);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// 3) Pre-settle
|
|
229
|
+
const preBody = JSON.stringify({
|
|
230
|
+
headerVO: { shopId, siteId },
|
|
231
|
+
itemVOList: [
|
|
232
|
+
{
|
|
233
|
+
skuId,
|
|
234
|
+
skuCode,
|
|
235
|
+
skuName,
|
|
236
|
+
skuQty: quantity,
|
|
237
|
+
skuSaleUnit: unit,
|
|
238
|
+
salePrice: price,
|
|
239
|
+
listPrice: price,
|
|
240
|
+
unitRate,
|
|
241
|
+
addTime,
|
|
242
|
+
stockQty,
|
|
243
|
+
minSaleUnit: unit,
|
|
244
|
+
selectFlag: true,
|
|
245
|
+
status: 0,
|
|
246
|
+
canBuyFlag: true,
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
balanceFlag: false,
|
|
250
|
+
userPointFlag: false,
|
|
251
|
+
});
|
|
252
|
+
const preUrl = http.moduleUrl("ORDER", "/app/order/preSettleOrder");
|
|
253
|
+
r = await http.request(preUrl, { method: "POST", headers, body: preBody });
|
|
254
|
+
if (!r.ok || !biz.isBizSuccess(r.json)) {
|
|
255
|
+
envelope(false, "pre-settle failed", { upstream: r.json }, 2);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
const pdata =
|
|
259
|
+
r.json && typeof r.json === "object" && r.json.data && typeof r.json.data === "object"
|
|
260
|
+
? r.json.data
|
|
261
|
+
: null;
|
|
262
|
+
const preSettleId = pdata && /** @type {{preSettleId?:string}} */ (pdata).preSettleId;
|
|
263
|
+
const headerVO = pdata && /** @type {{headerVO?:unknown}} */ (pdata).headerVO;
|
|
264
|
+
const itemVOList = pdata && /** @type {{itemVOList?:unknown}} */ (pdata).itemVOList;
|
|
265
|
+
if (!preSettleId) {
|
|
266
|
+
envelope(false, "missing preSettleId", { upstream: r.json }, 2);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// 4) Confirm
|
|
271
|
+
const confirmBody = JSON.stringify({
|
|
272
|
+
preSettleId,
|
|
273
|
+
headerVO,
|
|
274
|
+
itemVOList,
|
|
275
|
+
balanceFlag: false,
|
|
276
|
+
userPointFlag: false,
|
|
277
|
+
validateStockFlag: true,
|
|
278
|
+
repeatFlag: false,
|
|
279
|
+
});
|
|
280
|
+
const confirmUrl = http.moduleUrl("ORDER", "/app/order/confirmOrder");
|
|
281
|
+
r = await http.request(confirmUrl, { method: "POST", headers, body: confirmBody });
|
|
282
|
+
if (!r.ok || !biz.isBizSuccess(r.json)) {
|
|
283
|
+
envelope(false, "confirm failed", { upstream: r.json }, 2);
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
const orderData =
|
|
287
|
+
r.json && typeof r.json === "object" && r.json.data && typeof r.json.data === "object"
|
|
288
|
+
? r.json.data
|
|
289
|
+
: {};
|
|
290
|
+
const orderId = /** @type {{orderId?:string,payAmt?:unknown}} */ (orderData).orderId;
|
|
291
|
+
const payAmt = /** @type {{orderId?:string,payAmt?:unknown}} */ (orderData).payAmt;
|
|
292
|
+
|
|
293
|
+
// 5) Pay URL
|
|
294
|
+
const memberUrl = http.moduleUrl("DEFAULT", "/member/getInfo/V2");
|
|
295
|
+
const h2 = /** @type {Record<string,string>} */ ({ ...headers });
|
|
296
|
+
delete h2["Content-Type"];
|
|
297
|
+
const uinfo = await http.request(memberUrl, { method: "GET", headers: h2, body: null });
|
|
298
|
+
let phone = String(cfg.phone || "");
|
|
299
|
+
let nick = "";
|
|
300
|
+
let img = "";
|
|
301
|
+
if (uinfo.ok && biz.isBizSuccess(uinfo.json) && uinfo.json && typeof uinfo.json === "object") {
|
|
302
|
+
const d = /** @type {{data?:Record<string,unknown>}} */ (uinfo.json).data;
|
|
303
|
+
if (d) {
|
|
304
|
+
phone = String(d.memberPhone || phone);
|
|
305
|
+
nick = String(d.memberNick || "");
|
|
306
|
+
img = String(d.headerImg || "");
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
const enc = encodeURIComponent;
|
|
310
|
+
const payUrlStr = `https://dhcmall.ifoodbuy.com/H5/#/replacePay?phone=${enc(phone)}&orderid=${enc(String(orderId))}&img=${enc(img)}&nick=${enc(nick)}&siteId=${siteId}&shopId=${shopId}&share=true`;
|
|
311
|
+
|
|
312
|
+
envelope(
|
|
313
|
+
true,
|
|
314
|
+
"order placed",
|
|
315
|
+
{
|
|
316
|
+
product: skuName,
|
|
317
|
+
quantity,
|
|
318
|
+
unit,
|
|
319
|
+
orderId,
|
|
320
|
+
payAmt,
|
|
321
|
+
payUrl: payUrlStr,
|
|
322
|
+
},
|
|
323
|
+
0,
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
module.exports = { preSettle, confirm, payUrl, quick };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const crypto = require("crypto");
|
|
4
|
+
const http = require("../http");
|
|
5
|
+
const config = require("../config");
|
|
6
|
+
const biz = require("../biz");
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {object} opts
|
|
10
|
+
*/
|
|
11
|
+
async function search(opts) {
|
|
12
|
+
const cfg = config.loadConfig();
|
|
13
|
+
if (!cfg?.token) {
|
|
14
|
+
console.error("cyy: not logged in. Run: cyy auth login");
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const shopId = Number(opts.shopId ?? cfg.shop_id);
|
|
19
|
+
const siteId = Number(opts.siteId ?? cfg.site_id);
|
|
20
|
+
const body = JSON.stringify({
|
|
21
|
+
pageNum: Number(opts.page || 1),
|
|
22
|
+
pageSize: Number(opts.pageSize || 10),
|
|
23
|
+
shopId,
|
|
24
|
+
siteId,
|
|
25
|
+
spuName: opts.keyword,
|
|
26
|
+
stockFlag: String(opts.stockFlag ?? "0"),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const url = http.moduleUrl("PRODUCT", "/app/product/getSkuList");
|
|
30
|
+
const headers = /** @type {Record<string,string>} */ (http.buildAuthHeaders(cfg));
|
|
31
|
+
|
|
32
|
+
const { ok, json } = await http.request(url, {
|
|
33
|
+
method: "POST",
|
|
34
|
+
headers,
|
|
35
|
+
body,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const traceId = `local-${crypto.randomBytes(8).toString("hex")}`;
|
|
39
|
+
const success = ok && biz.isBizSuccess(json);
|
|
40
|
+
console.log(
|
|
41
|
+
JSON.stringify(
|
|
42
|
+
{
|
|
43
|
+
success,
|
|
44
|
+
code: success ? "OK" : "UPSTREAM_ERROR",
|
|
45
|
+
message: success ? "success" : "search failed",
|
|
46
|
+
data: { upstream: json },
|
|
47
|
+
traceId,
|
|
48
|
+
},
|
|
49
|
+
null,
|
|
50
|
+
2,
|
|
51
|
+
),
|
|
52
|
+
);
|
|
53
|
+
process.exit(success ? 0 : 2);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = { search };
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const http = require("http");
|
|
4
|
+
const { executeApiCall } = require("./apiCall");
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param {{ port: number, host: string }} opts
|
|
8
|
+
*/
|
|
9
|
+
function runServe(opts) {
|
|
10
|
+
const port = Number(opts.port ?? 8787);
|
|
11
|
+
const host = String(opts.host || "127.0.0.1");
|
|
12
|
+
|
|
13
|
+
const server = http.createServer(async (req, res) => {
|
|
14
|
+
if (req.method !== "POST" || req.url !== "/invoke") {
|
|
15
|
+
res.writeHead(404, { "Content-Type": "application/json; charset=utf-8" });
|
|
16
|
+
res.end(JSON.stringify({ error: "not_found", hint: "POST /invoke only" }));
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let raw = "";
|
|
21
|
+
for await (const chunk of req) {
|
|
22
|
+
raw += chunk;
|
|
23
|
+
}
|
|
24
|
+
let payload = {};
|
|
25
|
+
try {
|
|
26
|
+
payload = raw ? JSON.parse(raw) : {};
|
|
27
|
+
} catch {
|
|
28
|
+
res.writeHead(400, { "Content-Type": "application/json; charset=utf-8" });
|
|
29
|
+
res.end(JSON.stringify({ error: "invalid_json" }));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const {
|
|
34
|
+
method = "GET",
|
|
35
|
+
module: moduleKey = "DEFAULT",
|
|
36
|
+
path: pathSuffix,
|
|
37
|
+
body: bodyObj,
|
|
38
|
+
query: queryObj,
|
|
39
|
+
noAuth = false,
|
|
40
|
+
} = payload;
|
|
41
|
+
|
|
42
|
+
if (!pathSuffix || typeof pathSuffix !== "string") {
|
|
43
|
+
res.writeHead(400, { "Content-Type": "application/json; charset=utf-8" });
|
|
44
|
+
res.end(JSON.stringify({ error: "path_required" }));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const result = await executeApiCall({
|
|
50
|
+
method: String(method).toUpperCase(),
|
|
51
|
+
module: moduleKey,
|
|
52
|
+
path: pathSuffix,
|
|
53
|
+
bodyObj,
|
|
54
|
+
queryObj: queryObj && typeof queryObj === "object" ? queryObj : undefined,
|
|
55
|
+
query: undefined,
|
|
56
|
+
noAuth: Boolean(noAuth),
|
|
57
|
+
header: [],
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
if (result.error) {
|
|
61
|
+
res.writeHead(400, { "Content-Type": "application/json; charset=utf-8" });
|
|
62
|
+
res.end(JSON.stringify({ error: result.error }));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
67
|
+
res.end(JSON.stringify(result.envelope));
|
|
68
|
+
} catch (e) {
|
|
69
|
+
res.writeHead(500, { "Content-Type": "application/json; charset=utf-8" });
|
|
70
|
+
res.end(
|
|
71
|
+
JSON.stringify({
|
|
72
|
+
error: "internal_error",
|
|
73
|
+
message: e instanceof Error ? e.message : String(e),
|
|
74
|
+
}),
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
server.listen(port, host, () => {
|
|
80
|
+
console.error(`cyy serve listening on http://${host}:${port} (POST /invoke)`);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = { runServe };
|