cyymall-cli 0.1.0 → 0.1.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.
@@ -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,131 @@ 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
+ if (json == null) {
138
+ console.error(`cyy: send login code returned empty response body (HTTP ${status}); SMS delivery is not confirmed`);
139
+ process.exit(2);
140
+ }
141
+
142
+ if (!biz.isBizSuccess(json)) {
143
+ const msg = extractBizMessage(json, "send login code failed");
144
+ console.error(`cyy: ${msg}`);
145
+ if (ok && json != null && typeof json === "object" && encrypt.looksLikeHybridEnvelope(json)) {
146
+ console.error(
147
+ "cyy: response is still hybrid-encrypted (decrypt failed). The gateway encrypted with a client public key that does not match the bundled private key, or Base64 fields were truncated. The CLI now only uses bundled keys from embeddedCyyKeys.js. Set CYY_ENCRYPT_DEBUG=1 for RSA/AES diagnostics. See app-api-cli-spec.md section 3.1.",
148
+ );
149
+ }
150
+ console.error(JSON.stringify(json, null, 2));
151
+ process.exit(2);
152
+ }
153
+
154
+ return { status, json };
155
+ }
156
+
157
+ async function sendCode(phone) {
158
+ const { status, json } = await requestSmsCode(phone);
159
+ const traceId = buildTraceId();
160
+ console.log(
161
+ JSON.stringify(
162
+ {
163
+ success: true,
164
+ code: "OK",
165
+ message: "code sent",
166
+ data: {
167
+ phone,
168
+ objectCode: "3",
169
+ httpStatus: status,
170
+ upstream: json,
171
+ },
172
+ traceId,
173
+ },
174
+ null,
175
+ 2,
176
+ ),
177
+ );
178
+ process.exit(0);
179
+ }
180
+
181
+ async function loginWithCode(phone, code) {
24
182
  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({
183
+ const headers = /** @type {Record<string,string>} */ (http.buildDefaultHeaders());
184
+ const plainBody = JSON.stringify({
27
185
  phone,
28
186
  objectCode: "3",
29
- password: sha256Hex(password),
30
- passwordLoginFlag: true,
31
- onKeyLoginFlag: false,
187
+ password: "",
188
+ passwordLoginFlag: false,
189
+ onKeyLoginFlag: true,
190
+ smsCode: code,
32
191
  wxOpenid: "",
33
192
  });
193
+ const body = buildHybridAuthBody(plainBody, headers);
34
194
 
35
- const { ok, json } = await http.request(url, {
195
+ const { ok, json: jsonAfterParse, rawWireText } = await http.request(url, {
36
196
  method: "POST",
37
197
  headers,
38
198
  body,
199
+ decryptHybridResponse: false,
200
+ includeRawWireText: true,
39
201
  });
40
202
 
203
+ const json = decryptAuthBizJson(rawWireText, jsonAfterParse);
204
+
41
205
  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";
206
+ const msg = extractBizMessage(json, "login failed");
46
207
  console.error(`cyy: ${msg}`);
208
+ if (json != null) console.error(JSON.stringify(json, null, 2));
47
209
  process.exit(2);
48
210
  }
49
211
 
@@ -74,7 +236,7 @@ async function login(phone, password) {
74
236
 
75
237
  config.saveConfig(saved);
76
238
 
77
- const traceId = `local-${crypto.randomBytes(8).toString("hex")}`;
239
+ const traceId = buildTraceId();
78
240
  console.log(
79
241
  JSON.stringify(
80
242
  {
@@ -98,10 +260,35 @@ async function login(phone, password) {
98
260
  process.exit(0);
99
261
  }
100
262
 
263
+ async function promptCode() {
264
+ const rl = readline.createInterface({ input, output });
265
+ try {
266
+ const answer = await rl.question("Enter SMS verification code: ");
267
+ return String(answer || "").trim();
268
+ } finally {
269
+ rl.close();
270
+ }
271
+ }
272
+
273
+ async function login(phone, code) {
274
+ if (code) {
275
+ await loginWithCode(phone, code);
276
+ return;
277
+ }
278
+
279
+ await requestSmsCode(phone);
280
+ const smsCode = await promptCode();
281
+ if (!smsCode) {
282
+ console.error("cyy: verification code is required");
283
+ process.exit(1);
284
+ }
285
+ await loginWithCode(phone, smsCode);
286
+ }
287
+
101
288
  async function whoami() {
102
289
  const cfg = config.loadConfig();
103
290
  if (!cfg?.token) {
104
- console.error("cyy: not logged in. Run: cyy auth login --phone ... --password ...");
291
+ console.error("cyy: not logged in. Run: cyy auth login --phone ...");
105
292
  process.exit(1);
106
293
  }
107
294
  const url = http.moduleUrl("DEFAULT", "/member/getInfo/V2");
@@ -113,7 +300,7 @@ async function whoami() {
113
300
  body: null,
114
301
  });
115
302
 
116
- const traceId = `local-${crypto.randomBytes(8).toString("hex")}`;
303
+ const traceId = buildTraceId();
117
304
  const success = ok && biz.isBizSuccess(json);
118
305
  console.log(
119
306
  JSON.stringify(
@@ -131,4 +318,4 @@ async function whoami() {
131
318
  process.exit(success ? 0 : 2);
132
319
  }
133
320
 
134
- module.exports = { login, whoami };
321
+ 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 };