cyymall-cli 0.1.0 → 0.1.2

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.
@@ -1,149 +1,149 @@
1
- "use strict";
2
-
3
- const config = require("../config");
4
- const http = require("../http");
5
- const biz = require("../biz");
6
-
7
- /**
8
- * Parse repeated --query key=value
9
- * @param {string[]} queryArgs
10
- */
11
- function parseQueryPairs(queryArgs) {
12
- const q = {};
13
- if (!queryArgs || !queryArgs.length) return q;
14
- for (const pair of queryArgs) {
15
- const i = pair.indexOf("=");
16
- if (i <= 0) continue;
17
- const k = pair.slice(0, i).trim();
18
- const v = pair.slice(i + 1);
19
- q[k] = v;
20
- }
21
- return q;
22
- }
23
-
24
- function appendQuery(url, queryObj) {
25
- const keys = Object.keys(queryObj);
26
- if (!keys.length) return url;
27
- const u = new URL(url);
28
- for (const k of keys) {
29
- u.searchParams.set(k, queryObj[k]);
30
- }
31
- return u.toString();
32
- }
33
-
34
- /**
35
- * Shared executor for CLI and HTTP serve mode.
36
- * @param {object} opts
37
- */
38
- async function executeApiCall(opts) {
39
- const {
40
- method,
41
- module: moduleKey,
42
- path: pathSuffix,
43
- bodyJson,
44
- bodyFile,
45
- bodyObj,
46
- query: queryArgs,
47
- queryObj: queryObjIn,
48
- noAuth,
49
- header: headerPairs,
50
- } = opts;
51
-
52
- let bodyStr = null;
53
- if (bodyFile) {
54
- const fs = require("fs");
55
- bodyStr = fs.readFileSync(bodyFile, "utf8");
56
- } else if (bodyObj != null) {
57
- bodyStr = JSON.stringify(bodyObj);
58
- } else if (bodyJson != null && bodyJson !== "") {
59
- bodyStr = bodyJson;
60
- }
61
-
62
- if (bodyStr && method === "GET") {
63
- return { error: "GET request should not use body; use query only.", exitCode: 1 };
64
- }
65
-
66
- let url = http.moduleUrl(moduleKey, pathSuffix);
67
- const queryObj =
68
- queryObjIn && typeof queryObjIn === "object"
69
- ? queryObjIn
70
- : parseQueryPairs(queryArgs || []);
71
- url = appendQuery(url, queryObj);
72
-
73
- /** @type {Record<string,string>} */
74
- let headers = {};
75
-
76
- if (!noAuth) {
77
- const cfg = config.loadConfig();
78
- headers = /** @type {Record<string,string>} */ (
79
- http.buildAuthHeaders(cfg)
80
- );
81
- } else {
82
- headers = /** @type {Record<string,string>} */ (http.buildAuthHeaders(null));
83
- }
84
-
85
- const extras = headerPairs || [];
86
- for (const h of extras) {
87
- const idx = h.indexOf(":");
88
- if (idx > 0) {
89
- const hk = h.slice(0, idx).trim();
90
- const hv = h.slice(idx + 1).trim();
91
- headers[hk] = hv;
92
- }
93
- }
94
-
95
- if (method === "GET" || method === "HEAD") {
96
- delete headers["Content-Type"];
97
- } else if (bodyStr) {
98
- headers["Content-Type"] = headers["Content-Type"] || "application/json;charset=UTF-8";
99
- }
100
-
101
- const { ok, status, json } = await http.request(url, {
102
- method,
103
- headers,
104
- body: bodyStr,
105
- });
106
-
107
- const bizOk = ok && (json == null || biz.isBizSuccess(json));
108
- const envelopeSuccess = bizOk;
109
- const traceId = `local-${require("crypto").randomBytes(8).toString("hex")}`;
110
- const message =
111
- json && typeof json === "object" && "msg" in json
112
- ? String(/** @type {{msg?:string}} */ (json).msg)
113
- : ok
114
- ? "success"
115
- : `HTTP ${status}`;
116
-
117
- const envelope = {
118
- success: envelopeSuccess,
119
- code: envelopeSuccess ? "OK" : "UPSTREAM_ERROR",
120
- message,
121
- data: { upstream: json, httpStatus: status },
122
- traceId,
123
- };
124
-
125
- return { envelope, exitCode: envelopeSuccess ? 0 : 2 };
126
- }
127
-
128
- /**
129
- * @param {import('commander').OptionValues} opts
130
- */
131
- async function runApiCall(opts) {
132
- if (opts.bodyJson && opts.method === "GET") {
133
- console.error("cyy: GET request should not use body; use --query instead.");
134
- process.exit(1);
135
- }
136
- const merged = {
137
- ...opts,
138
- noAuth: Boolean(opts.noAuth ?? opts.bare),
139
- };
140
- const result = await executeApiCall(merged);
141
- if (result.error) {
142
- console.error(`cyy: ${result.error}`);
143
- process.exit(result.exitCode ?? 1);
144
- }
145
- console.log(JSON.stringify(result.envelope, null, 2));
146
- process.exit(result.exitCode ?? 1);
147
- }
148
-
149
- module.exports = { runApiCall, executeApiCall, parseQueryPairs };
1
+ "use strict";
2
+
3
+ const config = require("../config");
4
+ const http = require("../http");
5
+ const biz = require("../biz");
6
+
7
+ /**
8
+ * Parse repeated --query key=value
9
+ * @param {string[]} queryArgs
10
+ */
11
+ function parseQueryPairs(queryArgs) {
12
+ const q = {};
13
+ if (!queryArgs || !queryArgs.length) return q;
14
+ for (const pair of queryArgs) {
15
+ const i = pair.indexOf("=");
16
+ if (i <= 0) continue;
17
+ const k = pair.slice(0, i).trim();
18
+ const v = pair.slice(i + 1);
19
+ q[k] = v;
20
+ }
21
+ return q;
22
+ }
23
+
24
+ function appendQuery(url, queryObj) {
25
+ const keys = Object.keys(queryObj);
26
+ if (!keys.length) return url;
27
+ const u = new URL(url);
28
+ for (const k of keys) {
29
+ u.searchParams.set(k, queryObj[k]);
30
+ }
31
+ return u.toString();
32
+ }
33
+
34
+ /**
35
+ * Shared executor for CLI and HTTP serve mode.
36
+ * @param {object} opts
37
+ */
38
+ async function executeApiCall(opts) {
39
+ const {
40
+ method,
41
+ module: moduleKey,
42
+ path: pathSuffix,
43
+ bodyJson,
44
+ bodyFile,
45
+ bodyObj,
46
+ query: queryArgs,
47
+ queryObj: queryObjIn,
48
+ noAuth,
49
+ header: headerPairs,
50
+ } = opts;
51
+
52
+ let bodyStr = null;
53
+ if (bodyFile) {
54
+ const fs = require("fs");
55
+ bodyStr = fs.readFileSync(bodyFile, "utf8");
56
+ } else if (bodyObj != null) {
57
+ bodyStr = JSON.stringify(bodyObj);
58
+ } else if (bodyJson != null && bodyJson !== "") {
59
+ bodyStr = bodyJson;
60
+ }
61
+
62
+ if (bodyStr && method === "GET") {
63
+ return { error: "GET request should not use body; use query only.", exitCode: 1 };
64
+ }
65
+
66
+ let url = http.moduleUrl(moduleKey, pathSuffix);
67
+ const queryObj =
68
+ queryObjIn && typeof queryObjIn === "object"
69
+ ? queryObjIn
70
+ : parseQueryPairs(queryArgs || []);
71
+ url = appendQuery(url, queryObj);
72
+
73
+ /** @type {Record<string,string>} */
74
+ let headers = {};
75
+
76
+ if (!noAuth) {
77
+ const cfg = config.loadConfig();
78
+ headers = /** @type {Record<string,string>} */ (
79
+ http.buildAuthHeaders(cfg)
80
+ );
81
+ } else {
82
+ headers = /** @type {Record<string,string>} */ (http.buildAuthHeaders(null));
83
+ }
84
+
85
+ const extras = headerPairs || [];
86
+ for (const h of extras) {
87
+ const idx = h.indexOf(":");
88
+ if (idx > 0) {
89
+ const hk = h.slice(0, idx).trim();
90
+ const hv = h.slice(idx + 1).trim();
91
+ headers[hk] = hv;
92
+ }
93
+ }
94
+
95
+ if (method === "GET" || method === "HEAD") {
96
+ delete headers["Content-Type"];
97
+ } else if (bodyStr) {
98
+ headers["Content-Type"] = headers["Content-Type"] || "application/json;charset=UTF-8";
99
+ }
100
+
101
+ const { ok, status, json } = await http.request(url, {
102
+ method,
103
+ headers,
104
+ body: bodyStr,
105
+ });
106
+
107
+ const bizOk = ok && (json == null || biz.isBizSuccess(json));
108
+ const envelopeSuccess = bizOk;
109
+ const traceId = `local-${require("crypto").randomBytes(8).toString("hex")}`;
110
+ const message =
111
+ json && typeof json === "object" && "msg" in json
112
+ ? String(/** @type {{msg?:string}} */ (json).msg)
113
+ : ok
114
+ ? "success"
115
+ : `HTTP ${status}`;
116
+
117
+ const envelope = {
118
+ success: envelopeSuccess,
119
+ code: envelopeSuccess ? "OK" : "UPSTREAM_ERROR",
120
+ message,
121
+ data: { upstream: json, httpStatus: status },
122
+ traceId,
123
+ };
124
+
125
+ return { envelope, exitCode: envelopeSuccess ? 0 : 2 };
126
+ }
127
+
128
+ /**
129
+ * @param {import('commander').OptionValues} opts
130
+ */
131
+ async function runApiCall(opts) {
132
+ if (opts.bodyJson && opts.method === "GET") {
133
+ console.error("cyy: GET request should not use body; use --query instead.");
134
+ process.exit(1);
135
+ }
136
+ const merged = {
137
+ ...opts,
138
+ noAuth: Boolean(opts.noAuth ?? opts.bare),
139
+ };
140
+ const result = await executeApiCall(merged);
141
+ if (result.error) {
142
+ console.error(`cyy: ${result.error}`);
143
+ process.exit(result.exitCode ?? 1);
144
+ }
145
+ console.log(JSON.stringify(result.envelope, null, 2));
146
+ process.exit(result.exitCode ?? 1);
147
+ }
148
+
149
+ module.exports = { runApiCall, executeApiCall, parseQueryPairs };
@@ -1,16 +1,77 @@
1
- "use strict";
1
+ "use strict";
2
2
 
3
3
  const crypto = require("crypto");
4
+ const readline = require("readline/promises");
5
+ const { stdin: input, stdout: output } = require("process");
4
6
  const config = require("../config");
5
7
  const http = require("../http");
6
8
  const biz = require("../biz");
9
+ const encrypt = require("../encrypt");
7
10
 
8
- function sha256Hex(s) {
9
- return crypto.createHash("sha256").update(s, "utf8").digest("hex");
11
+ /**
12
+ * `/app/auth/ua/*` responses (e.g. sendCodeV2, registerMember/v2): raw wire -> decryptResponse ->
13
+ * JSON.parse -> optional parsedJsonAfterHybridEnvelopeDecrypt, same order as App.
14
+ *
15
+ * @param {string} [rawWireText]
16
+ * @param {unknown} jsonAfterParse
17
+ * @param {string} clientPkForDecrypt
18
+ * @returns {unknown}
19
+ */
20
+ function plainBizJsonFromSendCodeWire(rawWireText, jsonAfterParse, clientPkForDecrypt) {
21
+ let wire =
22
+ typeof rawWireText === "string" && rawWireText.trim()
23
+ ? rawWireText.trim()
24
+ : jsonAfterParse &&
25
+ typeof jsonAfterParse === "object" &&
26
+ jsonAfterParse !== null &&
27
+ !Array.isArray(jsonAfterParse) &&
28
+ Object.prototype.hasOwnProperty.call(jsonAfterParse, "_raw") &&
29
+ typeof /** @type {{ _raw?: unknown }} */ (jsonAfterParse)._raw === "string"
30
+ ? String(/** @type {{ _raw: string }} */ (jsonAfterParse)._raw).trim()
31
+ : "";
32
+
33
+ if (
34
+ !wire &&
35
+ jsonAfterParse != null &&
36
+ typeof jsonAfterParse === "object" &&
37
+ !Array.isArray(jsonAfterParse) &&
38
+ encrypt.looksLikeHybridEnvelope(jsonAfterParse)
39
+ ) {
40
+ try {
41
+ wire = JSON.stringify(jsonAfterParse);
42
+ } catch {
43
+ wire = "";
44
+ }
45
+ }
46
+
47
+ if (wire) {
48
+ const decryptedText = encrypt.decryptResponse(wire, clientPkForDecrypt);
49
+ try {
50
+ const obj = decryptedText ? JSON.parse(decryptedText) : null;
51
+ if (obj != null && typeof obj === "object" && !Array.isArray(obj)) {
52
+ const out = encrypt.looksLikeHybridEnvelope(obj)
53
+ ? encrypt.parsedJsonAfterHybridEnvelopeDecrypt(obj, clientPkForDecrypt)
54
+ : obj;
55
+ if (!encrypt.looksLikeHybridEnvelope(out)) {
56
+ return out;
57
+ }
58
+ }
59
+ } catch {
60
+ // fall through to parsed-json fallback
61
+ }
62
+ }
63
+
64
+ if (jsonAfterParse != null && typeof jsonAfterParse === "object" && !Array.isArray(jsonAfterParse)) {
65
+ return encrypt.looksLikeHybridEnvelope(jsonAfterParse)
66
+ ? encrypt.parsedJsonAfterHybridEnvelopeDecrypt(jsonAfterParse, clientPkForDecrypt)
67
+ : jsonAfterParse;
68
+ }
69
+
70
+ return jsonAfterParse;
10
71
  }
11
72
 
12
73
  /**
13
- * Login `data` may use `token` (Android LoginBean / Gson), `appToken`, or `accessToken`.
74
+ * Login `data` may use `token`, `appToken`, or `accessToken`.
14
75
  * @param {Record<string, unknown>|null|undefined} data
15
76
  */
16
77
  function pickLoginToken(data) {
@@ -20,30 +81,118 @@ function pickLoginToken(data) {
20
81
  return v != null ? String(v) : "";
21
82
  }
22
83
 
23
- async function login(phone, password) {
84
+ function buildTraceId() {
85
+ return `local-${crypto.randomBytes(8).toString("hex")}`;
86
+ }
87
+
88
+ function extractBizMessage(json, fallback) {
89
+ return json && typeof json === "object" && "msg" in json
90
+ ? String(/** @type {{msg?:string}} */ (json).msg || fallback)
91
+ : fallback;
92
+ }
93
+
94
+ function buildHybridAuthBody(plainBody, headers) {
95
+ if (!encrypt.encryptEnvConfigured()) {
96
+ return plainBody;
97
+ }
98
+ headers.encrypte = "true";
99
+ const { serverPublicKey } = encrypt.resolveEncryptKeys();
100
+ const hy = encrypt.hybridEncrypt(plainBody, serverPublicKey);
101
+ return encrypt.hybridEncryptResultToJson(hy);
102
+ }
103
+
104
+ function decryptAuthBizJson(rawWireText, jsonAfterParse) {
105
+ const { clientPrivateKey } = encrypt.resolveEncryptKeys();
106
+ if (!clientPrivateKey) return jsonAfterParse;
107
+ return plainBizJsonFromSendCodeWire(
108
+ typeof rawWireText === "string" ? rawWireText : "",
109
+ jsonAfterParse,
110
+ clientPrivateKey,
111
+ );
112
+ }
113
+
114
+ async function requestSmsCode(phone) {
115
+ const url = http.moduleUrl("DEFAULT", "/app/auth/ua/sendCodeV2");
116
+ const headers = /** @type {Record<string,string>} */ (http.buildDefaultHeaders());
117
+ const plainBody = JSON.stringify({ phone, objectCode: "3" });
118
+ const body = buildHybridAuthBody(plainBody, headers);
119
+
120
+ const { ok, status, json: jsonAfterParse, rawWireText } = await http.request(url, {
121
+ method: "POST",
122
+ headers,
123
+ body,
124
+ decryptHybridResponse: false,
125
+ includeRawWireText: true,
126
+ });
127
+
128
+ const json = decryptAuthBizJson(rawWireText, jsonAfterParse);
129
+
130
+ if (!ok) {
131
+ const msg = extractBizMessage(json, `send login code failed (HTTP ${status})`);
132
+ console.error(`cyy: ${msg}`);
133
+ if (json != null) console.error(JSON.stringify(json, null, 2));
134
+ process.exit(2);
135
+ }
136
+
137
+ // User-requested behavior: once the send-code request itself succeeds over HTTP,
138
+ // treat SMS dispatch as successful even if the encrypted response body cannot be
139
+ // decrypted into upstream business JSON.
140
+ return { status, json, acceptedByGateway: true };
141
+ }
142
+
143
+ async function sendCode(phone) {
144
+ const { status, json, acceptedByGateway } = await requestSmsCode(phone);
145
+ const traceId = buildTraceId();
146
+ console.log(
147
+ JSON.stringify(
148
+ {
149
+ success: true,
150
+ code: "OK",
151
+ message: acceptedByGateway ? "code sent" : "code send accepted",
152
+ data: {
153
+ phone,
154
+ objectCode: "3",
155
+ httpStatus: status,
156
+ gatewayAccepted: acceptedByGateway,
157
+ upstream: json,
158
+ },
159
+ traceId,
160
+ },
161
+ null,
162
+ 2,
163
+ ),
164
+ );
165
+ process.exit(0);
166
+ }
167
+
168
+ async function loginWithCode(phone, code) {
24
169
  const url = http.moduleUrl("DEFAULT", "/app/auth/ua/registerMember/v2");
25
- const headers = /** @type {Record<string,string>} */ (http.buildAuthHeaders(null));
26
- const body = JSON.stringify({
170
+ const headers = /** @type {Record<string,string>} */ (http.buildDefaultHeaders());
171
+ const plainBody = JSON.stringify({
27
172
  phone,
28
173
  objectCode: "3",
29
- password: sha256Hex(password),
30
- passwordLoginFlag: true,
31
- onKeyLoginFlag: false,
174
+ password: "",
175
+ passwordLoginFlag: false,
176
+ onKeyLoginFlag: true,
177
+ smsCode: code,
32
178
  wxOpenid: "",
33
179
  });
180
+ const body = buildHybridAuthBody(plainBody, headers);
34
181
 
35
- const { ok, json } = await http.request(url, {
182
+ const { ok, json: jsonAfterParse, rawWireText } = await http.request(url, {
36
183
  method: "POST",
37
184
  headers,
38
185
  body,
186
+ decryptHybridResponse: false,
187
+ includeRawWireText: true,
39
188
  });
40
189
 
190
+ const json = decryptAuthBizJson(rawWireText, jsonAfterParse);
191
+
41
192
  if (!ok || !biz.isBizSuccess(json)) {
42
- const msg =
43
- json && typeof json === "object" && "msg" in json
44
- ? String(/** @type {{msg?:string}} */ (json).msg)
45
- : "login failed";
193
+ const msg = extractBizMessage(json, "login failed");
46
194
  console.error(`cyy: ${msg}`);
195
+ if (json != null) console.error(JSON.stringify(json, null, 2));
47
196
  process.exit(2);
48
197
  }
49
198
 
@@ -74,7 +223,7 @@ async function login(phone, password) {
74
223
 
75
224
  config.saveConfig(saved);
76
225
 
77
- const traceId = `local-${crypto.randomBytes(8).toString("hex")}`;
226
+ const traceId = buildTraceId();
78
227
  console.log(
79
228
  JSON.stringify(
80
229
  {
@@ -98,10 +247,35 @@ async function login(phone, password) {
98
247
  process.exit(0);
99
248
  }
100
249
 
250
+ async function promptCode() {
251
+ const rl = readline.createInterface({ input, output });
252
+ try {
253
+ const answer = await rl.question("Enter SMS verification code: ");
254
+ return String(answer || "").trim();
255
+ } finally {
256
+ rl.close();
257
+ }
258
+ }
259
+
260
+ async function login(phone, code) {
261
+ if (code) {
262
+ await loginWithCode(phone, code);
263
+ return;
264
+ }
265
+
266
+ await requestSmsCode(phone);
267
+ const smsCode = await promptCode();
268
+ if (!smsCode) {
269
+ console.error("cyy: verification code is required");
270
+ process.exit(1);
271
+ }
272
+ await loginWithCode(phone, smsCode);
273
+ }
274
+
101
275
  async function whoami() {
102
276
  const cfg = config.loadConfig();
103
277
  if (!cfg?.token) {
104
- console.error("cyy: not logged in. Run: cyy auth login --phone ... --password ...");
278
+ console.error("cyy: not logged in. Run: cyy auth login --phone ...");
105
279
  process.exit(1);
106
280
  }
107
281
  const url = http.moduleUrl("DEFAULT", "/member/getInfo/V2");
@@ -113,7 +287,7 @@ async function whoami() {
113
287
  body: null,
114
288
  });
115
289
 
116
- const traceId = `local-${crypto.randomBytes(8).toString("hex")}`;
290
+ const traceId = buildTraceId();
117
291
  const success = ok && biz.isBizSuccess(json);
118
292
  console.log(
119
293
  JSON.stringify(
@@ -131,4 +305,4 @@ async function whoami() {
131
305
  process.exit(success ? 0 : 2);
132
306
  }
133
307
 
134
- module.exports = { login, whoami };
308
+ module.exports = { login, loginWithCode, sendCode, whoami };
@@ -1,55 +1,55 @@
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
- /**
10
- * @param {{ bodyFile?: string, bodyJson?: string }} opts
11
- */
12
- async function add(opts) {
13
- const cfg = config.loadConfig();
14
- if (!cfg?.token) {
15
- console.error("cyy: not logged in.");
16
- process.exit(1);
17
- }
18
-
19
- let raw = opts.bodyJson;
20
- if (opts.bodyFile) {
21
- raw = fs.readFileSync(opts.bodyFile, "utf8");
22
- }
23
- if (!raw) {
24
- console.error("cyy: provide --body-file or --body-json");
25
- process.exit(1);
26
- }
27
-
28
- const url = http.moduleUrl("ORDER", "/app/order/cart");
29
- const headers = /** @type {Record<string,string>} */ (http.buildAuthHeaders(cfg));
30
-
31
- const { ok, json } = await http.request(url, {
32
- method: "POST",
33
- headers,
34
- body: raw,
35
- });
36
-
37
- const traceId = `local-${crypto.randomBytes(8).toString("hex")}`;
38
- const success = ok && biz.isBizSuccess(json);
39
- console.log(
40
- JSON.stringify(
41
- {
42
- success,
43
- code: success ? "OK" : "UPSTREAM_ERROR",
44
- message: success ? "success" : "cart failed",
45
- data: { upstream: json },
46
- traceId,
47
- },
48
- null,
49
- 2,
50
- ),
51
- );
52
- process.exit(success ? 0 : 2);
53
- }
54
-
55
- module.exports = { add };
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
+ /**
10
+ * @param {{ bodyFile?: string, bodyJson?: string }} opts
11
+ */
12
+ async function add(opts) {
13
+ const cfg = config.loadConfig();
14
+ if (!cfg?.token) {
15
+ console.error("cyy: not logged in.");
16
+ process.exit(1);
17
+ }
18
+
19
+ let raw = opts.bodyJson;
20
+ if (opts.bodyFile) {
21
+ raw = fs.readFileSync(opts.bodyFile, "utf8");
22
+ }
23
+ if (!raw) {
24
+ console.error("cyy: provide --body-file or --body-json");
25
+ process.exit(1);
26
+ }
27
+
28
+ const url = http.moduleUrl("ORDER", "/app/order/cart");
29
+ const headers = /** @type {Record<string,string>} */ (http.buildAuthHeaders(cfg));
30
+
31
+ const { ok, json } = await http.request(url, {
32
+ method: "POST",
33
+ headers,
34
+ body: raw,
35
+ });
36
+
37
+ const traceId = `local-${crypto.randomBytes(8).toString("hex")}`;
38
+ const success = ok && biz.isBizSuccess(json);
39
+ console.log(
40
+ JSON.stringify(
41
+ {
42
+ success,
43
+ code: success ? "OK" : "UPSTREAM_ERROR",
44
+ message: success ? "success" : "cart failed",
45
+ data: { upstream: json },
46
+ traceId,
47
+ },
48
+ null,
49
+ 2,
50
+ ),
51
+ );
52
+ process.exit(success ? 0 : 2);
53
+ }
54
+
55
+ module.exports = { add };