@huoye/app-ship 0.1.1 → 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.
package/dist/index.cjs CHANGED
@@ -53,23 +53,517 @@ __export(index_exports, {
53
53
  });
54
54
  module.exports = __toCommonJS(index_exports);
55
55
 
56
+ // node_modules/chalk/source/vendor/ansi-styles/index.js
57
+ var ANSI_BACKGROUND_OFFSET = 10;
58
+ var wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`;
59
+ var wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`;
60
+ var wrapAnsi16m = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`;
61
+ var styles = {
62
+ modifier: {
63
+ reset: [0, 0],
64
+ // 21 isn't widely supported and 22 does the same thing
65
+ bold: [1, 22],
66
+ dim: [2, 22],
67
+ italic: [3, 23],
68
+ underline: [4, 24],
69
+ overline: [53, 55],
70
+ inverse: [7, 27],
71
+ hidden: [8, 28],
72
+ strikethrough: [9, 29]
73
+ },
74
+ color: {
75
+ black: [30, 39],
76
+ red: [31, 39],
77
+ green: [32, 39],
78
+ yellow: [33, 39],
79
+ blue: [34, 39],
80
+ magenta: [35, 39],
81
+ cyan: [36, 39],
82
+ white: [37, 39],
83
+ // Bright color
84
+ blackBright: [90, 39],
85
+ gray: [90, 39],
86
+ // Alias of `blackBright`
87
+ grey: [90, 39],
88
+ // Alias of `blackBright`
89
+ redBright: [91, 39],
90
+ greenBright: [92, 39],
91
+ yellowBright: [93, 39],
92
+ blueBright: [94, 39],
93
+ magentaBright: [95, 39],
94
+ cyanBright: [96, 39],
95
+ whiteBright: [97, 39]
96
+ },
97
+ bgColor: {
98
+ bgBlack: [40, 49],
99
+ bgRed: [41, 49],
100
+ bgGreen: [42, 49],
101
+ bgYellow: [43, 49],
102
+ bgBlue: [44, 49],
103
+ bgMagenta: [45, 49],
104
+ bgCyan: [46, 49],
105
+ bgWhite: [47, 49],
106
+ // Bright color
107
+ bgBlackBright: [100, 49],
108
+ bgGray: [100, 49],
109
+ // Alias of `bgBlackBright`
110
+ bgGrey: [100, 49],
111
+ // Alias of `bgBlackBright`
112
+ bgRedBright: [101, 49],
113
+ bgGreenBright: [102, 49],
114
+ bgYellowBright: [103, 49],
115
+ bgBlueBright: [104, 49],
116
+ bgMagentaBright: [105, 49],
117
+ bgCyanBright: [106, 49],
118
+ bgWhiteBright: [107, 49]
119
+ }
120
+ };
121
+ var modifierNames = Object.keys(styles.modifier);
122
+ var foregroundColorNames = Object.keys(styles.color);
123
+ var backgroundColorNames = Object.keys(styles.bgColor);
124
+ var colorNames = [...foregroundColorNames, ...backgroundColorNames];
125
+ function assembleStyles() {
126
+ const codes = /* @__PURE__ */ new Map();
127
+ for (const [groupName, group] of Object.entries(styles)) {
128
+ for (const [styleName, style] of Object.entries(group)) {
129
+ styles[styleName] = {
130
+ open: `\x1B[${style[0]}m`,
131
+ close: `\x1B[${style[1]}m`
132
+ };
133
+ group[styleName] = styles[styleName];
134
+ codes.set(style[0], style[1]);
135
+ }
136
+ Object.defineProperty(styles, groupName, {
137
+ value: group,
138
+ enumerable: false
139
+ });
140
+ }
141
+ Object.defineProperty(styles, "codes", {
142
+ value: codes,
143
+ enumerable: false
144
+ });
145
+ styles.color.close = "\x1B[39m";
146
+ styles.bgColor.close = "\x1B[49m";
147
+ styles.color.ansi = wrapAnsi16();
148
+ styles.color.ansi256 = wrapAnsi256();
149
+ styles.color.ansi16m = wrapAnsi16m();
150
+ styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET);
151
+ styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET);
152
+ styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);
153
+ Object.defineProperties(styles, {
154
+ rgbToAnsi256: {
155
+ value(red, green, blue) {
156
+ if (red === green && green === blue) {
157
+ if (red < 8) {
158
+ return 16;
159
+ }
160
+ if (red > 248) {
161
+ return 231;
162
+ }
163
+ return Math.round((red - 8) / 247 * 24) + 232;
164
+ }
165
+ return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5);
166
+ },
167
+ enumerable: false
168
+ },
169
+ hexToRgb: {
170
+ value(hex) {
171
+ const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16));
172
+ if (!matches) {
173
+ return [0, 0, 0];
174
+ }
175
+ let [colorString] = matches;
176
+ if (colorString.length === 3) {
177
+ colorString = [...colorString].map((character) => character + character).join("");
178
+ }
179
+ const integer = Number.parseInt(colorString, 16);
180
+ return [
181
+ /* eslint-disable no-bitwise */
182
+ integer >> 16 & 255,
183
+ integer >> 8 & 255,
184
+ integer & 255
185
+ /* eslint-enable no-bitwise */
186
+ ];
187
+ },
188
+ enumerable: false
189
+ },
190
+ hexToAnsi256: {
191
+ value: (hex) => styles.rgbToAnsi256(...styles.hexToRgb(hex)),
192
+ enumerable: false
193
+ },
194
+ ansi256ToAnsi: {
195
+ value(code) {
196
+ if (code < 8) {
197
+ return 30 + code;
198
+ }
199
+ if (code < 16) {
200
+ return 90 + (code - 8);
201
+ }
202
+ let red;
203
+ let green;
204
+ let blue;
205
+ if (code >= 232) {
206
+ red = ((code - 232) * 10 + 8) / 255;
207
+ green = red;
208
+ blue = red;
209
+ } else {
210
+ code -= 16;
211
+ const remainder = code % 36;
212
+ red = Math.floor(code / 36) / 5;
213
+ green = Math.floor(remainder / 6) / 5;
214
+ blue = remainder % 6 / 5;
215
+ }
216
+ const value = Math.max(red, green, blue) * 2;
217
+ if (value === 0) {
218
+ return 30;
219
+ }
220
+ let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red));
221
+ if (value === 2) {
222
+ result += 60;
223
+ }
224
+ return result;
225
+ },
226
+ enumerable: false
227
+ },
228
+ rgbToAnsi: {
229
+ value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)),
230
+ enumerable: false
231
+ },
232
+ hexToAnsi: {
233
+ value: (hex) => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)),
234
+ enumerable: false
235
+ }
236
+ });
237
+ return styles;
238
+ }
239
+ var ansiStyles = assembleStyles();
240
+ var ansi_styles_default = ansiStyles;
241
+
242
+ // node_modules/chalk/source/vendor/supports-color/index.js
243
+ var import_node_process = __toESM(require("process"), 1);
244
+ var import_node_os = __toESM(require("os"), 1);
245
+ var import_node_tty = __toESM(require("tty"), 1);
246
+ function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : import_node_process.default.argv) {
247
+ const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
248
+ const position = argv.indexOf(prefix + flag);
249
+ const terminatorPosition = argv.indexOf("--");
250
+ return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
251
+ }
252
+ var { env } = import_node_process.default;
253
+ var flagForceColor;
254
+ if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
255
+ flagForceColor = 0;
256
+ } else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
257
+ flagForceColor = 1;
258
+ }
259
+ function envForceColor() {
260
+ if ("FORCE_COLOR" in env) {
261
+ if (env.FORCE_COLOR === "true") {
262
+ return 1;
263
+ }
264
+ if (env.FORCE_COLOR === "false") {
265
+ return 0;
266
+ }
267
+ return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);
268
+ }
269
+ }
270
+ function translateLevel(level) {
271
+ if (level === 0) {
272
+ return false;
273
+ }
274
+ return {
275
+ level,
276
+ hasBasic: true,
277
+ has256: level >= 2,
278
+ has16m: level >= 3
279
+ };
280
+ }
281
+ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
282
+ const noFlagForceColor = envForceColor();
283
+ if (noFlagForceColor !== void 0) {
284
+ flagForceColor = noFlagForceColor;
285
+ }
286
+ const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
287
+ if (forceColor === 0) {
288
+ return 0;
289
+ }
290
+ if (sniffFlags) {
291
+ if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
292
+ return 3;
293
+ }
294
+ if (hasFlag("color=256")) {
295
+ return 2;
296
+ }
297
+ }
298
+ if ("TF_BUILD" in env && "AGENT_NAME" in env) {
299
+ return 1;
300
+ }
301
+ if (haveStream && !streamIsTTY && forceColor === void 0) {
302
+ return 0;
303
+ }
304
+ const min = forceColor || 0;
305
+ if (env.TERM === "dumb") {
306
+ return min;
307
+ }
308
+ if (import_node_process.default.platform === "win32") {
309
+ const osRelease = import_node_os.default.release().split(".");
310
+ if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
311
+ return Number(osRelease[2]) >= 14931 ? 3 : 2;
312
+ }
313
+ return 1;
314
+ }
315
+ if ("CI" in env) {
316
+ if (["GITHUB_ACTIONS", "GITEA_ACTIONS", "CIRCLECI"].some((key) => key in env)) {
317
+ return 3;
318
+ }
319
+ if (["TRAVIS", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE"].some((sign) => sign in env) || env.CI_NAME === "codeship") {
320
+ return 1;
321
+ }
322
+ return min;
323
+ }
324
+ if ("TEAMCITY_VERSION" in env) {
325
+ return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
326
+ }
327
+ if (env.COLORTERM === "truecolor") {
328
+ return 3;
329
+ }
330
+ if (env.TERM === "xterm-kitty") {
331
+ return 3;
332
+ }
333
+ if (env.TERM === "xterm-ghostty") {
334
+ return 3;
335
+ }
336
+ if (env.TERM === "wezterm") {
337
+ return 3;
338
+ }
339
+ if ("TERM_PROGRAM" in env) {
340
+ const version = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
341
+ switch (env.TERM_PROGRAM) {
342
+ case "iTerm.app": {
343
+ return version >= 3 ? 3 : 2;
344
+ }
345
+ case "Apple_Terminal": {
346
+ return 2;
347
+ }
348
+ }
349
+ }
350
+ if (/-256(color)?$/i.test(env.TERM)) {
351
+ return 2;
352
+ }
353
+ if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
354
+ return 1;
355
+ }
356
+ if ("COLORTERM" in env) {
357
+ return 1;
358
+ }
359
+ return min;
360
+ }
361
+ function createSupportsColor(stream, options = {}) {
362
+ const level = _supportsColor(stream, {
363
+ streamIsTTY: stream && stream.isTTY,
364
+ ...options
365
+ });
366
+ return translateLevel(level);
367
+ }
368
+ var supportsColor = {
369
+ stdout: createSupportsColor({ isTTY: import_node_tty.default.isatty(1) }),
370
+ stderr: createSupportsColor({ isTTY: import_node_tty.default.isatty(2) })
371
+ };
372
+ var supports_color_default = supportsColor;
373
+
374
+ // node_modules/chalk/source/utilities.js
375
+ function stringReplaceAll(string, substring, replacer) {
376
+ let index = string.indexOf(substring);
377
+ if (index === -1) {
378
+ return string;
379
+ }
380
+ const substringLength = substring.length;
381
+ let endIndex = 0;
382
+ let returnValue = "";
383
+ do {
384
+ returnValue += string.slice(endIndex, index) + substring + replacer;
385
+ endIndex = index + substringLength;
386
+ index = string.indexOf(substring, endIndex);
387
+ } while (index !== -1);
388
+ returnValue += string.slice(endIndex);
389
+ return returnValue;
390
+ }
391
+ function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) {
392
+ let endIndex = 0;
393
+ let returnValue = "";
394
+ do {
395
+ const gotCR = string[index - 1] === "\r";
396
+ returnValue += string.slice(endIndex, gotCR ? index - 1 : index) + prefix + (gotCR ? "\r\n" : "\n") + postfix;
397
+ endIndex = index + 1;
398
+ index = string.indexOf("\n", endIndex);
399
+ } while (index !== -1);
400
+ returnValue += string.slice(endIndex);
401
+ return returnValue;
402
+ }
403
+
404
+ // node_modules/chalk/source/index.js
405
+ var { stdout: stdoutColor, stderr: stderrColor } = supports_color_default;
406
+ var GENERATOR = /* @__PURE__ */ Symbol("GENERATOR");
407
+ var STYLER = /* @__PURE__ */ Symbol("STYLER");
408
+ var IS_EMPTY = /* @__PURE__ */ Symbol("IS_EMPTY");
409
+ var levelMapping = [
410
+ "ansi",
411
+ "ansi",
412
+ "ansi256",
413
+ "ansi16m"
414
+ ];
415
+ var styles2 = /* @__PURE__ */ Object.create(null);
416
+ var applyOptions = (object, options = {}) => {
417
+ if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) {
418
+ throw new Error("The `level` option should be an integer from 0 to 3");
419
+ }
420
+ const colorLevel = stdoutColor ? stdoutColor.level : 0;
421
+ object.level = options.level === void 0 ? colorLevel : options.level;
422
+ };
423
+ var chalkFactory = (options) => {
424
+ const chalk2 = (...strings) => strings.join(" ");
425
+ applyOptions(chalk2, options);
426
+ Object.setPrototypeOf(chalk2, createChalk.prototype);
427
+ return chalk2;
428
+ };
429
+ function createChalk(options) {
430
+ return chalkFactory(options);
431
+ }
432
+ Object.setPrototypeOf(createChalk.prototype, Function.prototype);
433
+ for (const [styleName, style] of Object.entries(ansi_styles_default)) {
434
+ styles2[styleName] = {
435
+ get() {
436
+ const builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]);
437
+ Object.defineProperty(this, styleName, { value: builder });
438
+ return builder;
439
+ }
440
+ };
441
+ }
442
+ styles2.visible = {
443
+ get() {
444
+ const builder = createBuilder(this, this[STYLER], true);
445
+ Object.defineProperty(this, "visible", { value: builder });
446
+ return builder;
447
+ }
448
+ };
449
+ var getModelAnsi = (model, level, type, ...arguments_) => {
450
+ if (model === "rgb") {
451
+ if (level === "ansi16m") {
452
+ return ansi_styles_default[type].ansi16m(...arguments_);
453
+ }
454
+ if (level === "ansi256") {
455
+ return ansi_styles_default[type].ansi256(ansi_styles_default.rgbToAnsi256(...arguments_));
456
+ }
457
+ return ansi_styles_default[type].ansi(ansi_styles_default.rgbToAnsi(...arguments_));
458
+ }
459
+ if (model === "hex") {
460
+ return getModelAnsi("rgb", level, type, ...ansi_styles_default.hexToRgb(...arguments_));
461
+ }
462
+ return ansi_styles_default[type][model](...arguments_);
463
+ };
464
+ var usedModels = ["rgb", "hex", "ansi256"];
465
+ for (const model of usedModels) {
466
+ styles2[model] = {
467
+ get() {
468
+ const { level } = this;
469
+ return function(...arguments_) {
470
+ const styler = createStyler(getModelAnsi(model, levelMapping[level], "color", ...arguments_), ansi_styles_default.color.close, this[STYLER]);
471
+ return createBuilder(this, styler, this[IS_EMPTY]);
472
+ };
473
+ }
474
+ };
475
+ const bgModel = "bg" + model[0].toUpperCase() + model.slice(1);
476
+ styles2[bgModel] = {
477
+ get() {
478
+ const { level } = this;
479
+ return function(...arguments_) {
480
+ const styler = createStyler(getModelAnsi(model, levelMapping[level], "bgColor", ...arguments_), ansi_styles_default.bgColor.close, this[STYLER]);
481
+ return createBuilder(this, styler, this[IS_EMPTY]);
482
+ };
483
+ }
484
+ };
485
+ }
486
+ var proto = Object.defineProperties(() => {
487
+ }, {
488
+ ...styles2,
489
+ level: {
490
+ enumerable: true,
491
+ get() {
492
+ return this[GENERATOR].level;
493
+ },
494
+ set(level) {
495
+ this[GENERATOR].level = level;
496
+ }
497
+ }
498
+ });
499
+ var createStyler = (open, close, parent) => {
500
+ let openAll;
501
+ let closeAll;
502
+ if (parent === void 0) {
503
+ openAll = open;
504
+ closeAll = close;
505
+ } else {
506
+ openAll = parent.openAll + open;
507
+ closeAll = close + parent.closeAll;
508
+ }
509
+ return {
510
+ open,
511
+ close,
512
+ openAll,
513
+ closeAll,
514
+ parent
515
+ };
516
+ };
517
+ var createBuilder = (self, _styler, _isEmpty) => {
518
+ const builder = (...arguments_) => applyStyle(builder, arguments_.length === 1 ? "" + arguments_[0] : arguments_.join(" "));
519
+ Object.setPrototypeOf(builder, proto);
520
+ builder[GENERATOR] = self;
521
+ builder[STYLER] = _styler;
522
+ builder[IS_EMPTY] = _isEmpty;
523
+ return builder;
524
+ };
525
+ var applyStyle = (self, string) => {
526
+ if (self.level <= 0 || !string) {
527
+ return self[IS_EMPTY] ? "" : string;
528
+ }
529
+ let styler = self[STYLER];
530
+ if (styler === void 0) {
531
+ return string;
532
+ }
533
+ const { openAll, closeAll } = styler;
534
+ if (string.includes("\x1B")) {
535
+ while (styler !== void 0) {
536
+ string = stringReplaceAll(string, styler.close, styler.open);
537
+ styler = styler.parent;
538
+ }
539
+ }
540
+ const lfIndex = string.indexOf("\n");
541
+ if (lfIndex !== -1) {
542
+ string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex);
543
+ }
544
+ return openAll + string + closeAll;
545
+ };
546
+ Object.defineProperties(createChalk.prototype, styles2);
547
+ var chalk = createChalk();
548
+ var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
549
+ var source_default = chalk;
550
+
56
551
  // src/utils/logger.ts
57
- var import_chalk = __toESM(require("chalk"), 1);
58
552
  var ConsoleLogger = class {
59
553
  info(message) {
60
- console.log(import_chalk.default.blue("\u2139\uFE0F " + message));
554
+ console.log(source_default.blue("\u2139\uFE0F " + message));
61
555
  }
62
556
  success(message) {
63
- console.log(import_chalk.default.green("\u2705 " + message));
557
+ console.log(source_default.green("\u2705 " + message));
64
558
  }
65
559
  warn(message) {
66
- console.log(import_chalk.default.yellow("\u26A0\uFE0F " + message));
560
+ console.log(source_default.yellow("\u26A0\uFE0F " + message));
67
561
  }
68
562
  error(message) {
69
- console.log(import_chalk.default.red("\u274C " + message));
563
+ console.log(source_default.red("\u274C " + message));
70
564
  }
71
565
  title(message) {
72
- console.log(import_chalk.default.bold.cyan("\n" + message + "\n"));
566
+ console.log(source_default.bold.cyan("\n" + message + "\n"));
73
567
  }
74
568
  newLine() {
75
569
  console.log("");
@@ -293,12 +787,7 @@ var RetryStrategy = class {
293
787
  logger.warn(`${operationName} failed (Attempt ${attempt}): ${errorMessage} [${category}]`);
294
788
  if (!this.shouldRetry(error, attempt)) {
295
789
  logger.error(`${operationName} failed with non-retryable error: ${errorMessage}`);
296
- return {
297
- success: false,
298
- error,
299
- totalAttempts: attempt,
300
- lastError: errorMessage
301
- };
790
+ throw error;
302
791
  }
303
792
  if (attempt <= this.config.maxRetries) {
304
793
  const delayMs = this.calculateDelay(attempt - 1);
@@ -309,12 +798,7 @@ var RetryStrategy = class {
309
798
  }
310
799
  }
311
800
  }
312
- return {
313
- success: false,
314
- error: lastError || new Error(`${operationName} failed after all retries`),
315
- totalAttempts: attempt,
316
- lastError: lastError?.message
317
- };
801
+ throw lastError || new Error(`${operationName} failed after all retries`);
318
802
  }
319
803
  };
320
804
  var DEFAULT_RETRY_CONFIG = {
@@ -508,6 +992,7 @@ async function parseHap(filePath) {
508
992
  }
509
993
 
510
994
  // src/utils/upload-manager.ts
995
+ var GROUPED_ARTIFACT_PLATFORMS = /* @__PURE__ */ new Set(["huawei", "xiaomi", "oppo", "vivo", "tencent"]);
511
996
  var UploadManager = class {
512
997
  uploaders;
513
998
  config;
@@ -614,9 +1099,22 @@ var UploadManager = class {
614
1099
  if (matchedFiles.length === 0) {
615
1100
  logger.warn(`[${platform}] No compatible file found in provided files`);
616
1101
  } else {
617
- matchedFiles.forEach((file) => {
618
- tasks.push({ platform, file });
619
- });
1102
+ if (this.shouldGroupArtifacts(platform)) {
1103
+ const artifactSelection = this.resolveArtifactSelection(platform, matchedFiles, metadata);
1104
+ if (!artifactSelection) {
1105
+ logger.warn(`[${platform}] Failed to build grouped artifact selection`);
1106
+ return;
1107
+ }
1108
+ tasks.push({
1109
+ platform,
1110
+ file: this.getRepresentativeFilePath(artifactSelection),
1111
+ artifactSelection
1112
+ });
1113
+ } else {
1114
+ matchedFiles.forEach((file) => {
1115
+ tasks.push({ platform, file, artifactSelection: file });
1116
+ });
1117
+ }
620
1118
  }
621
1119
  });
622
1120
  if (tasks.length === 0) {
@@ -635,7 +1133,13 @@ var UploadManager = class {
635
1133
  logger.info(`Scheduled ${tasks.length} upload task(s) for ${validPlatforms.length} platform(s)`);
636
1134
  const uploadPromises = tasks.map((task) => {
637
1135
  logger.info(`[${task.platform}] Matched file: ${import_path2.default.basename(task.file)}`);
638
- return this.uploadToPlatform(task.platform, task.file, config, metadata);
1136
+ return this.uploadToPlatform(
1137
+ task.platform,
1138
+ task.file,
1139
+ config,
1140
+ metadata,
1141
+ task.artifactSelection
1142
+ );
639
1143
  });
640
1144
  const results = await Promise.all(uploadPromises);
641
1145
  const report = this.aggregateResults(results, tasks.length, startTime);
@@ -645,7 +1149,7 @@ var UploadManager = class {
645
1149
  /**
646
1150
  * 上传到单个平台
647
1151
  */
648
- async uploadToPlatform(platform, filePath, config, metadata) {
1152
+ async uploadToPlatform(platform, filePath, config, metadata, artifactSelection = filePath) {
649
1153
  if (this.config.cancellationToken?.isCancelled) {
650
1154
  logger.warn(`[${platform.toUpperCase()}] Upload skipped due to cancellation`);
651
1155
  return {
@@ -666,25 +1170,30 @@ var UploadManager = class {
666
1170
  if (this.config.verbose) {
667
1171
  logger.info(`[${platform.toUpperCase()}] Starting upload...`);
668
1172
  }
1173
+ const effectiveMetadata = {
1174
+ ...metadata,
1175
+ artifactPath: artifactSelection
1176
+ };
1177
+ await this.validateArtifactSelection(platform, artifactSelection, effectiveMetadata);
669
1178
  const pkgInfo = await parsePackageInfo(filePath, {
670
- androidSdkPath: metadata.globalSettings?.androidSdkPath
1179
+ androidSdkPath: effectiveMetadata.globalSettings?.androidSdkPath
671
1180
  });
672
1181
  if (pkgInfo) {
673
1182
  if (pkgInfo.versionName) {
674
- metadata.version = pkgInfo.versionName;
1183
+ effectiveMetadata.version = pkgInfo.versionName;
675
1184
  }
676
1185
  if (pkgInfo.versionCode) {
677
- metadata.buildNumber = pkgInfo.versionCode;
1186
+ effectiveMetadata.buildNumber = pkgInfo.versionCode;
678
1187
  }
679
1188
  if (pkgInfo.packageName) {
680
1189
  ;
681
- metadata.packageName = pkgInfo.packageName;
1190
+ effectiveMetadata.packageName = pkgInfo.packageName;
682
1191
  }
683
1192
  logger.info(
684
- `[${platform.toUpperCase()}] \u5DF2\u6821\u51C6\u7248\u672C\u4FE1\u606F: v${metadata.version}+${metadata.buildNumber}`
1193
+ `[${platform.toUpperCase()}] \u5DF2\u6821\u51C6\u7248\u672C\u4FE1\u606F: v${effectiveMetadata.version}+${effectiveMetadata.buildNumber}`
685
1194
  );
686
1195
  }
687
- const result = await uploader.upload(filePath, config, metadata);
1196
+ const result = await uploader.upload(filePath, config, effectiveMetadata);
688
1197
  return result;
689
1198
  } catch (error) {
690
1199
  const errorMessage = error.message || String(error);
@@ -697,6 +1206,85 @@ var UploadManager = class {
697
1206
  };
698
1207
  }
699
1208
  }
1209
+ shouldGroupArtifacts(platform) {
1210
+ return GROUPED_ARTIFACT_PLATFORMS.has(platform);
1211
+ }
1212
+ resolveArtifactSelection(platform, matchedFiles, metadata) {
1213
+ const explicitArtifacts = metadata.platformArtifacts?.[platform];
1214
+ if (explicitArtifacts) {
1215
+ if (typeof explicitArtifacts === "string") {
1216
+ return explicitArtifacts;
1217
+ }
1218
+ if (Object.keys(explicitArtifacts).length > 0) {
1219
+ return explicitArtifacts;
1220
+ }
1221
+ }
1222
+ if (matchedFiles.length === 0) {
1223
+ return null;
1224
+ }
1225
+ if (matchedFiles.length === 1) {
1226
+ return matchedFiles[0];
1227
+ }
1228
+ return this.buildArtifactMapFromFiles(matchedFiles);
1229
+ }
1230
+ buildArtifactMapFromFiles(files) {
1231
+ return files.reduce(
1232
+ (acc, filePath, index) => {
1233
+ acc[this.inferArtifactKey(filePath, index)] = filePath;
1234
+ return acc;
1235
+ },
1236
+ {}
1237
+ );
1238
+ }
1239
+ inferArtifactKey(filePath, index) {
1240
+ const fileName = import_path2.default.basename(filePath).toLowerCase();
1241
+ if (fileName.includes("arm64-v8a")) return "arm64-v8a";
1242
+ if (fileName.includes("armeabi-v7a")) return "armeabi-v7a";
1243
+ if (fileName.includes("x86_64")) return "x86_64";
1244
+ if (fileName.includes("universal")) return "universal";
1245
+ return `artifact_${index + 1}`;
1246
+ }
1247
+ getRepresentativeFilePath(artifactSelection) {
1248
+ if (typeof artifactSelection === "string") {
1249
+ return artifactSelection;
1250
+ }
1251
+ return Object.values(artifactSelection)[0];
1252
+ }
1253
+ async validateArtifactSelection(platform, artifactSelection, metadata) {
1254
+ if (typeof artifactSelection === "string") {
1255
+ return;
1256
+ }
1257
+ const artifactPaths = Object.values(artifactSelection).filter(Boolean);
1258
+ if (artifactPaths.length < 2) {
1259
+ return;
1260
+ }
1261
+ const parsedInfos = await Promise.all(
1262
+ artifactPaths.map(
1263
+ (artifactPath) => parsePackageInfo(artifactPath, {
1264
+ androidSdkPath: metadata.globalSettings?.androidSdkPath
1265
+ })
1266
+ )
1267
+ );
1268
+ const availableInfos = parsedInfos.filter((info) => !!info);
1269
+ if (availableInfos.length < 2) {
1270
+ logger.warn(
1271
+ `[${platform.toUpperCase()}] Unable to fully validate multi-artifact package info; continuing with upload.`
1272
+ );
1273
+ return;
1274
+ }
1275
+ const [baseInfo, ...restInfos] = availableInfos;
1276
+ for (const info of restInfos) {
1277
+ if (baseInfo?.packageName && info?.packageName && baseInfo.packageName !== info.packageName) {
1278
+ throw new Error("\u53CC\u5305\u4E0A\u4F20\u5931\u8D25\uFF1A\u6240\u9009\u5B89\u88C5\u5305\u7684 packageName \u4E0D\u4E00\u81F4");
1279
+ }
1280
+ if (baseInfo?.versionName && info?.versionName && baseInfo.versionName !== info.versionName) {
1281
+ throw new Error("\u53CC\u5305\u4E0A\u4F20\u5931\u8D25\uFF1A\u6240\u9009\u5B89\u88C5\u5305\u7684 versionName \u4E0D\u4E00\u81F4");
1282
+ }
1283
+ if (baseInfo?.versionCode && info?.versionCode && baseInfo.versionCode !== info.versionCode) {
1284
+ throw new Error("\u53CC\u5305\u4E0A\u4F20\u5931\u8D25\uFF1A\u6240\u9009\u5B89\u88C5\u5305\u7684 versionCode \u4E0D\u4E00\u81F4");
1285
+ }
1286
+ }
1287
+ }
700
1288
  /**
701
1289
  * 聚合上传结果
702
1290
  */
@@ -1162,27 +1750,39 @@ var HuaweiUploader = class _HuaweiUploader {
1162
1750
  clientId,
1163
1751
  appId
1164
1752
  };
1165
- logger.info("Requesting upload URL...");
1166
- const uploadInfoResult = await retryStrategy.execute(
1167
- () => this.getUploadUrl(ctx, suffix),
1168
- "Huawei get upload URL"
1169
- );
1170
- if (!uploadInfoResult.success || !uploadInfoResult.data) {
1171
- throw uploadInfoResult.error || new Error("Failed to get upload URL");
1172
- }
1173
- const uploadInfo = uploadInfoResult.data;
1174
- logger.info("Uploading file (this may take a while)...");
1175
- const uploadFileResult = await retryStrategy.execute(
1176
- () => this.uploadFile(uploadInfo.uploadUrl, uploadInfo.authCode, filePath),
1177
- "Huawei file upload"
1178
- );
1179
- if (!uploadFileResult.success || !uploadFileResult.data) {
1180
- throw uploadFileResult.error || new Error("File upload failed");
1753
+ const artifacts = typeof metadata.artifactPath === "object" ? metadata.artifactPath : { universal: filePath || metadata.artifactPath || "" };
1754
+ logger.info(`[Huawei] \u5904\u7406\u4EA7\u7269\u6570\u91CF: ${Object.keys(artifacts).length}`);
1755
+ const uploadResults = [];
1756
+ const uploadPromises = Object.entries(artifacts).map(async ([abi, p]) => {
1757
+ if (!p || !import_fs_extra4.default.existsSync(p)) return;
1758
+ const currentSuffix = import_path3.default.extname(p).substring(1);
1759
+ logger.info(`[Huawei] \u6B63\u5728\u4E0A\u4F20 ${abi} \u5305: ${import_path3.default.basename(p)}`);
1760
+ const uploadInfo = await retryStrategy.execute(
1761
+ () => this.getUploadUrl(ctx, currentSuffix),
1762
+ `Huawei get upload URL for ${abi}`
1763
+ );
1764
+ if (!uploadInfo.success || !uploadInfo.data) {
1765
+ throw uploadInfo.error || new Error(`Failed to get upload URL for ${abi}`);
1766
+ }
1767
+ const uploadFileRes = await retryStrategy.execute(
1768
+ () => this.executeUploadRequest(uploadInfo.data.uploadUrl, uploadInfo.data.authCode, p),
1769
+ `Huawei upload file for ${abi}`
1770
+ );
1771
+ if (!uploadFileRes.success || !uploadFileRes.data || uploadFileRes.data.length === 0) {
1772
+ throw uploadFileRes.error || new Error(`File upload failed for ${abi}`);
1773
+ }
1774
+ uploadResults.push({
1775
+ fileName: import_path3.default.basename(p),
1776
+ fileDestUrl: uploadFileRes.data[0].fileDestUrl
1777
+ });
1778
+ });
1779
+ await Promise.all(uploadPromises);
1780
+ if (uploadResults.length === 0) {
1781
+ throw new Error("No files were successfully uploaded");
1181
1782
  }
1182
- const fileDestUrl = uploadFileResult.data.fileDestUrl;
1183
- logger.info("Linking file to app...");
1783
+ logger.info(`[Huawei] \u6B63\u5728\u5173\u8054 ${uploadResults.length} \u4E2A\u6587\u4EF6\u5230\u5E94\u7528...`);
1184
1784
  const updateInfoResult = await retryStrategy.execute(
1185
- () => this.updateAppFileInfo(ctx, fileDestUrl, filePath),
1785
+ () => this.updateAppFileInfo(ctx, uploadResults, filePath),
1186
1786
  "Huawei update file info"
1187
1787
  );
1188
1788
  if (!updateInfoResult.success) {
@@ -1386,14 +1986,17 @@ var HuaweiUploader = class _HuaweiUploader {
1386
1986
  throw new Error("Upload succeeded but fileDestUrl not found in response");
1387
1987
  }
1388
1988
  /** 更新应用文件信息 */
1389
- async updateAppFileInfo(ctx, fileDestUrl, filePath) {
1390
- const fileName = import_path3.default.basename(filePath);
1391
- const fileType = import_path3.default.extname(filePath).toLowerCase() === ".aab" ? 7 : 5;
1989
+ async updateAppFileInfo(ctx, files, anyFilePath) {
1990
+ const hasAab = files.some((f) => f.fileName.toLowerCase().endsWith(".aab"));
1991
+ const fileType = hasAab ? 7 /* AAB */ : 5 /* APP_PACKAGE */;
1392
1992
  const res = await import_axios2.default.put(
1393
1993
  `${ctx.apiRoot}/app-file-info`,
1394
1994
  {
1395
1995
  fileType,
1396
- files: [{ fileName, fileDestUrl }]
1996
+ files: files.map((f) => ({
1997
+ fileName: f.fileName,
1998
+ fileDestUrl: f.fileDestUrl
1999
+ }))
1397
2000
  },
1398
2001
  {
1399
2002
  params: { appId: ctx.appId },
@@ -1563,6 +2166,7 @@ var HuaweiUploader = class _HuaweiUploader {
1563
2166
 
1564
2167
  // src/adapters/xiaomi_uploader.ts
1565
2168
  var import_fs_extra5 = __toESM(require("fs-extra"), 1);
2169
+ var import_path4 = __toESM(require("path"), 1);
1566
2170
  var import_axios3 = __toESM(require("axios"), 1);
1567
2171
  var import_form_data3 = __toESM(require("form-data"), 1);
1568
2172
  var import_crypto3 = __toESM(require("crypto"), 1);
@@ -1813,80 +2417,39 @@ ${lines.join("\n")}
1813
2417
  const xiaomiConfig = config.publish?.xiaomi;
1814
2418
  const { userName, privateKey, publicKeyPath } = xiaomiConfig.auth;
1815
2419
  const updateDesc = metadata.changelog || xiaomiConfig?.description || "App Update";
1816
- const retryConfig = config.retry;
1817
- if (!import_fs_extra5.default.existsSync(filePath)) {
1818
- return {
1819
- platform: "xiaomi",
1820
- success: false,
1821
- message: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`
1822
- };
1823
- }
2420
+ const artifacts = typeof metadata.artifactPath === "object" ? metadata.artifactPath : { universal: filePath || (typeof metadata.artifactPath === "string" ? metadata.artifactPath : "") };
1824
2421
  const resolvedPublicKey = await this.resolvePublicKey(publicKeyPath);
1825
2422
  if (!resolvedPublicKey) {
1826
- return {
1827
- platform: "xiaomi",
1828
- success: false,
1829
- message: "\u65E0\u6CD5\u83B7\u53D6 RSA \u516C\u94A5: publicKeyPath \u672A\u63D0\u4F9B\u6216\u6587\u4EF6\u4E0D\u5B58\u5728"
1830
- };
1831
- }
1832
- const packageName = xiaomiConfig.packageName || this.inferPackageName(filePath);
1833
- if (!packageName) {
1834
- return {
1835
- platform: "xiaomi",
1836
- success: false,
1837
- message: "\u7F3A\u5C11 packageName \u914D\u7F6E, \u4E14\u65E0\u6CD5\u4ECE\u6587\u4EF6\u540D\u63A8\u65AD\u5E94\u7528\u5305\u540D"
1838
- };
1839
- }
1840
- const appName = xiaomiConfig.appName || this.inferAppName(packageName);
1841
- if (!appName) {
1842
- return {
1843
- platform: "xiaomi",
1844
- success: false,
1845
- message: "\u7F3A\u5C11 appName \u914D\u7F6E, \u4E14\u65E0\u6CD5\u4ECE\u5305\u540D\u63A8\u65AD\u5E94\u7528\u540D\u79F0"
1846
- };
2423
+ throw new Error("\u65E0\u6CD5\u83B7\u53D6 RSA \u516C\u94A5");
1847
2424
  }
1848
- logger.info("\u5F00\u59CB\u4E0A\u4F20\u5230\u5C0F\u7C73\u5E94\u7528\u5546\u5E97...");
1849
- logger.info(`\u6587\u4EF6: ${filePath}`);
1850
- logger.info(`\u5305\u540D: ${packageName}`);
1851
- logger.info(`\u5E94\u7528\u540D: ${appName}`);
1852
2425
  const publicKeyObj = this.extractPublicKey(resolvedPublicKey);
1853
- const configUploadType = xiaomiConfig.uploadType || "auto";
1854
- let uploadType;
1855
- let existingAppInfo = null;
1856
- if (configUploadType === "auto") {
1857
- logger.info("\u6B63\u5728\u67E5\u8BE2\u5E94\u7528\u4FE1\u606F...");
1858
- const status = await this.queryAppStatus(userName, packageName, privateKey, publicKeyObj);
1859
- uploadType = status.type;
1860
- existingAppInfo = status.appInfo;
1861
- } else {
1862
- uploadType = configUploadType;
1863
- }
1864
- const uploadTypeDesc = uploadType === "update" ? "\u5E94\u7528\u66F4\u65B0 (synchroType=1)" : "\u65B0\u589E\u5E94\u7528 (synchroType=0)";
1865
- logger.info(`\u4E0A\u4F20\u7C7B\u578B: ${uploadTypeDesc}`);
1866
- if (uploadType === "new" && xiaomiConfig.uploadType === "new") {
1867
- const missing = [];
1868
- if (xiaomiConfig.category == null) missing.push("category (\u5206\u7C7BID)");
1869
- if (!xiaomiConfig.desc) missing.push("desc (\u5E94\u7528\u4ECB\u7ECD)");
1870
- if (missing.length > 0) {
1871
- return {
1872
- platform: "xiaomi",
1873
- success: false,
1874
- message: `\u9996\u6B21\u63D0\u5BA1\u6A21\u5F0F\u7F3A\u5C11\u5FC5\u586B\u5B57\u6BB5: ${missing.join(", ")}\u3002\u8BF7\u5728\u540E\u53F0\u8865\u5168\u6216\u5728\u914D\u7F6E\u4E2D\u6DFB\u52A0\u3002`
1875
- };
1876
- }
1877
- }
2426
+ const anyPath = Object.values(artifacts)[0];
2427
+ const packageName = xiaomiConfig.packageName || this.inferPackageName(anyPath);
2428
+ if (!packageName) throw new Error("\u7F3A\u5C11 packageName");
2429
+ const appName = xiaomiConfig.appName || this.inferAppName(packageName);
1878
2430
  try {
1879
- const retryStrategy = createUploadRetryStrategy(retryConfig);
2431
+ logger.info(`\u5F00\u59CB\u4E0A\u4F20\u5230\u5C0F\u7C73\u5E94\u7528\u5546\u5E97 (\u5305\u542B ${Object.keys(artifacts).length} \u4E2A\u5305)...`);
2432
+ let uploadType = xiaomiConfig.uploadType || "auto";
2433
+ let finalUploadType;
2434
+ let existingAppInfo = null;
2435
+ if (uploadType === "auto") {
2436
+ const status = await this.queryAppStatus(userName, packageName, privateKey, publicKeyObj);
2437
+ finalUploadType = status.type;
2438
+ existingAppInfo = status.appInfo;
2439
+ } else {
2440
+ finalUploadType = uploadType;
2441
+ }
2442
+ const retryStrategy = createUploadRetryStrategy(config.retry);
1880
2443
  const result = await retryStrategy.execute(
1881
2444
  () => this.performUpload({
1882
- filePath,
2445
+ artifacts,
1883
2446
  userName,
1884
2447
  privateKey,
1885
2448
  publicKey: resolvedPublicKey,
2449
+ uploadType: finalUploadType,
1886
2450
  packageName,
1887
- appName,
2451
+ appName: appName || "",
1888
2452
  updateDesc,
1889
- uploadType,
1890
2453
  category: xiaomiConfig.category || existingAppInfo?.category,
1891
2454
  keyWords: xiaomiConfig.keyWords || existingAppInfo?.keyWords,
1892
2455
  desc: xiaomiConfig.desc || existingAppInfo?.desc,
@@ -1896,50 +2459,19 @@ ${lines.join("\n")}
1896
2459
  }),
1897
2460
  "Xiaomi push upload"
1898
2461
  );
1899
- if (!result.success) {
1900
- throw result.error || new Error("Upload failed");
1901
- }
1902
- logger.success("\u4E0A\u4F20\u5230\u5C0F\u7C73\u5E94\u7528\u5546\u5E97\u6210\u529F!");
1903
2462
  return {
1904
2463
  platform: "xiaomi",
1905
2464
  success: true,
1906
- message: "\u4E0A\u4F20\u6210\u529F, \u7B49\u5F85\u5BA1\u6838",
1907
- data: result.data,
2465
+ message: "\u4E0A\u4F20\u6210\u529F",
1908
2466
  consoleUrl: "https://dev.mi.com/distribute/app/details"
1909
2467
  };
1910
2468
  } catch (error) {
1911
- logger.error(`\u4E0A\u4F20\u5230\u5C0F\u7C73\u5931\u8D25: ${error.message}`);
1912
- if (error.response?.data) {
1913
- logger.info(`\u9519\u8BEF\u8BE6\u60C5: ${JSON.stringify(error.response.data)}`);
1914
- }
1915
- return {
1916
- platform: "xiaomi",
1917
- success: false,
1918
- message: error.message,
1919
- error
1920
- };
2469
+ logger.error(`\u5C0F\u7C73\u4E0A\u4F20\u5931\u8D25: ${error.message}`);
2470
+ return { platform: "xiaomi", success: false, message: error.message, error };
1921
2471
  }
1922
2472
  }
1923
- /**
1924
- * 执行实际的上传操作
1925
- */
1926
2473
  async performUpload(params) {
1927
- const {
1928
- filePath,
1929
- userName,
1930
- privateKey,
1931
- publicKey,
1932
- packageName,
1933
- appName,
1934
- updateDesc,
1935
- uploadType,
1936
- category,
1937
- keyWords,
1938
- desc,
1939
- brief,
1940
- privacyUrl,
1941
- assets
1942
- } = params;
2474
+ const { artifacts, userName, privateKey, publicKey, packageName, appName, updateDesc, uploadType, category, keyWords, desc, brief, privacyUrl, assets } = params;
1943
2475
  const publicKeyObj = this.extractPublicKey(publicKey);
1944
2476
  const requestData = this.buildRequestData({
1945
2477
  userName,
@@ -1953,67 +2485,55 @@ ${lines.join("\n")}
1953
2485
  brief,
1954
2486
  privacyUrl
1955
2487
  });
1956
- logger.info("RequestData \u5DF2\u6784\u5EFA");
1957
- logger.info(`RequestData: ${requestData}`);
1958
2488
  const requestDataMD5 = this.calculateStringMD5(requestData);
1959
- const apkFileMD5 = await this.calculateFileMD5(filePath);
1960
- logger.info(`APK MD5: ${apkFileMD5}`);
2489
+ let mainPath;
2490
+ let secondPath = void 0;
2491
+ const artifactEntries = Object.entries(artifacts);
2492
+ const arm64 = artifactEntries.find(([abi]) => abi === "arm64-v8a");
2493
+ const arm32 = artifactEntries.find(([abi]) => abi === "armeabi-v7a");
2494
+ if (arm64 && arm32) {
2495
+ mainPath = arm64[1];
2496
+ secondPath = arm32[1];
2497
+ logger.info(`[Xiaomi] \u8BC6\u522B\u5230\u591A\u67B6\u6784\u5206\u5305: \u4E3B\u5305(64\u4F4D)=${import_path4.default.basename(mainPath)}, \u6B21\u5305(32\u4F4D)=${import_path4.default.basename(secondPath)}`);
2498
+ } else {
2499
+ mainPath = artifactEntries[0][1];
2500
+ if (artifactEntries.length > 1) {
2501
+ secondPath = artifactEntries[1][1];
2502
+ logger.info(`[Xiaomi] \u8BC6\u522B\u5230\u591A\u4E2A\u5305\uFF08\u975E\u6807\u51C6\u540D\uFF09\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u987A\u5E8F: \u4E3B\u5305=${import_path4.default.basename(mainPath)}, \u6B21\u5305=${import_path4.default.basename(secondPath)}`);
2503
+ } else {
2504
+ logger.info(`[Xiaomi] \u4F7F\u7528\u5355\u5305\u6A21\u5F0F: ${import_path4.default.basename(mainPath)}`);
2505
+ }
2506
+ }
2507
+ const mainMD5 = await this.calculateFileMD5(mainPath);
1961
2508
  const extraSigItems = [];
1962
- let iconPath;
2509
+ if (secondPath) {
2510
+ const secondMD5 = await this.calculateFileMD5(secondPath);
2511
+ extraSigItems.push({ name: "secondApk", hash: secondMD5 });
2512
+ }
1963
2513
  if (assets?.icon && import_fs_extra5.default.existsSync(assets.icon)) {
1964
- iconPath = assets.icon;
1965
- const iconMD5 = await this.calculateFileMD5(iconPath);
1966
- extraSigItems.push({ name: "icon", hash: iconMD5 });
1967
- }
1968
- const screenshotPaths = [];
1969
- if (assets?.screenshots) {
1970
- for (let i = 0; i < assets.screenshots.length && i < 4; i++) {
1971
- const ssPath = assets.screenshots[i];
1972
- if (import_fs_extra5.default.existsSync(ssPath)) {
1973
- screenshotPaths.push(ssPath);
1974
- const ssMD5 = await this.calculateFileMD5(ssPath);
1975
- extraSigItems.push({ name: `screenshot_${i + 1}`, hash: ssMD5 });
1976
- }
1977
- }
2514
+ extraSigItems.push({ name: "icon", hash: await this.calculateFileMD5(assets.icon) });
1978
2515
  }
1979
- const sig = this.generateUploadSIG(
1980
- requestDataMD5,
1981
- apkFileMD5,
1982
- privateKey,
1983
- publicKeyObj,
1984
- extraSigItems.length > 0 ? extraSigItems : void 0
1985
- );
1986
- logger.info("SIG \u7B7E\u540D\u5DF2\u751F\u6210");
2516
+ const sig = this.generateUploadSIG(requestDataMD5, mainMD5, privateKey, publicKeyObj, extraSigItems);
1987
2517
  const form = new import_form_data3.default();
1988
2518
  form.append("RequestData", requestData);
1989
2519
  form.append("SIG", sig);
1990
- form.append("apk", import_fs_extra5.default.createReadStream(filePath));
1991
- if (iconPath) {
1992
- form.append("icon", import_fs_extra5.default.createReadStream(iconPath));
1993
- logger.info(`\u9644\u52A0 icon: ${iconPath}`);
2520
+ form.append("apk", import_fs_extra5.default.createReadStream(mainPath));
2521
+ if (secondPath) {
2522
+ form.append("secondApk", import_fs_extra5.default.createReadStream(secondPath));
1994
2523
  }
1995
- for (let i = 0; i < screenshotPaths.length; i++) {
1996
- form.append(`screenshot_${i + 1}`, import_fs_extra5.default.createReadStream(screenshotPaths[i]));
1997
- logger.info(`\u9644\u52A0 screenshot_${i + 1}: ${screenshotPaths[i]}`);
2524
+ if (assets?.icon && import_fs_extra5.default.existsSync(assets.icon)) {
2525
+ form.append("icon", import_fs_extra5.default.createReadStream(assets.icon));
1998
2526
  }
1999
- logger.info(`POST ${_XiaomiUploader.PUSH_API}`);
2000
2527
  const response = await import_axios3.default.post(_XiaomiUploader.PUSH_API, form, {
2001
- headers: {
2002
- ...form.getHeaders()
2003
- },
2528
+ headers: form.getHeaders(),
2004
2529
  maxContentLength: Infinity,
2005
2530
  maxBodyLength: Infinity,
2006
- timeout: 6e4
2007
- // 60 秒请求超时
2531
+ timeout: 12e4
2008
2532
  });
2009
- const data = response.data;
2010
- logger.info(`\u5C0F\u7C73 API \u539F\u59CB\u54CD\u5E94: ${JSON.stringify(data)}`);
2011
- if (data?.result !== 0) {
2012
- const errMsg = data?.message || data?.msg || JSON.stringify(data);
2013
- throw new Error(`\u5C0F\u7C73 API \u8FD4\u56DE\u9519\u8BEF (result=${data?.result}): ${errMsg}`);
2533
+ if (response.data?.result !== 0) {
2534
+ throw new Error(`\u5C0F\u7C73 API \u9519\u8BEF: ${response.data?.message || JSON.stringify(response.data)}`);
2014
2535
  }
2015
- logger.info(`\u5C0F\u7C73 API \u54CD\u5E94: ${JSON.stringify(data)}`);
2016
- return data;
2536
+ return response.data;
2017
2537
  }
2018
2538
  /**
2019
2539
  * 解析公钥: 优先使用内容字段, 其次从文件路径读取
@@ -2055,7 +2575,7 @@ ${lines.join("\n")}
2055
2575
 
2056
2576
  // src/adapters/apple_store_uploader.ts
2057
2577
  var import_fs_extra6 = __toESM(require("fs-extra"), 1);
2058
- var import_path4 = __toESM(require("path"), 1);
2578
+ var import_path5 = __toESM(require("path"), 1);
2059
2579
  var import_child_process = require("child_process");
2060
2580
  var import_util2 = require("util");
2061
2581
  var import_axios4 = __toESM(require("axios"), 1);
@@ -2221,7 +2741,7 @@ var AppleStoreUploader = class {
2221
2741
  appPassword,
2222
2742
  "-showTransferRate"
2223
2743
  ].join(" ");
2224
- logger.info(`[Transporter] Uploading: ${import_path4.default.basename(ipaPath)}`);
2744
+ logger.info(`[Transporter] Uploading: ${import_path5.default.basename(ipaPath)}`);
2225
2745
  try {
2226
2746
  const { stdout, stderr } = await execAsync(command, {
2227
2747
  timeout: 30 * 60 * 1e3,
@@ -2272,7 +2792,7 @@ Stderr: ${error.stderr || ""}`);
2272
2792
  });
2273
2793
  command = innerArgs.join(" ");
2274
2794
  }
2275
- logger.info(`[altool] Uploading: ${import_path4.default.basename(ipaPath)}`);
2795
+ logger.info(`[altool] Uploading: ${import_path5.default.basename(ipaPath)}`);
2276
2796
  logger.info(`[altool] Command prefix: ${tool.command}`);
2277
2797
  try {
2278
2798
  const { stdout, stderr } = await execAsync(command, {
@@ -2856,7 +3376,7 @@ var TestFlightUploader = class _TestFlightUploader {
2856
3376
 
2857
3377
  // src/adapters/adhoc_uploader.ts
2858
3378
  var import_fs_extra8 = __toESM(require("fs-extra"), 1);
2859
- var import_path5 = __toESM(require("path"), 1);
3379
+ var import_path6 = __toESM(require("path"), 1);
2860
3380
  var import_child_process2 = require("child_process");
2861
3381
  var AdHocUploader = class {
2862
3382
  name = "AdHoc";
@@ -2989,8 +3509,8 @@ var AdHocUploader = class {
2989
3509
  * @param changelog 更新日志
2990
3510
  */
2991
3511
  async createManifest(ipaPath, version, bundleId, changelog) {
2992
- const ipaFileName = import_path5.default.basename(ipaPath);
2993
- const appName = bundleId || import_path5.default.parse(ipaFileName).name;
3512
+ const ipaFileName = import_path6.default.basename(ipaPath);
3513
+ const appName = bundleId || import_path6.default.parse(ipaFileName).name;
2994
3514
  const ipaUrl = `itms-services://?action=download-manifest&url=https://your-distribution-server.com/manifests/${ipaFileName}.plist`;
2995
3515
  const manifestContent = `<?xml version="1.0" encoding="UTF-8"?>
2996
3516
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -3049,14 +3569,14 @@ var AdHocUploader = class {
3049
3569
  */
3050
3570
  async setupDistributionDirectory(ipaPath, manifestPath) {
3051
3571
  const projectRoot = process.cwd();
3052
- const distributionDir = import_path5.default.join(projectRoot, "build", "adhoc-distribution");
3572
+ const distributionDir = import_path6.default.join(projectRoot, "build", "adhoc-distribution");
3053
3573
  await import_fs_extra8.default.ensureDir(distributionDir);
3054
- await import_fs_extra8.default.ensureDir(import_path5.default.join(distributionDir, "apps"));
3055
- await import_fs_extra8.default.ensureDir(import_path5.default.join(distributionDir, "manifests"));
3056
- await import_fs_extra8.default.ensureDir(import_path5.default.join(distributionDir, "icons"));
3057
- const ipaFileName = import_path5.default.basename(ipaPath);
3058
- await import_fs_extra8.default.copy(ipaPath, import_path5.default.join(distributionDir, "apps", ipaFileName));
3059
- await import_fs_extra8.default.copy(manifestPath, import_path5.default.join(distributionDir, "manifests", `${ipaFileName}.plist`));
3574
+ await import_fs_extra8.default.ensureDir(import_path6.default.join(distributionDir, "apps"));
3575
+ await import_fs_extra8.default.ensureDir(import_path6.default.join(distributionDir, "manifests"));
3576
+ await import_fs_extra8.default.ensureDir(import_path6.default.join(distributionDir, "icons"));
3577
+ const ipaFileName = import_path6.default.basename(ipaPath);
3578
+ await import_fs_extra8.default.copy(ipaPath, import_path6.default.join(distributionDir, "apps", ipaFileName));
3579
+ await import_fs_extra8.default.copy(manifestPath, import_path6.default.join(distributionDir, "manifests", `${ipaFileName}.plist`));
3060
3580
  logger.info(`Distribution directory: ${distributionDir}`);
3061
3581
  return distributionDir;
3062
3582
  }
@@ -3066,7 +3586,7 @@ var AdHocUploader = class {
3066
3586
  * @param bundleId Bundle ID
3067
3587
  */
3068
3588
  generateInstallUrl(distributionPath, bundleId) {
3069
- const manifestPath = import_path5.default.join(distributionPath, "manifests", `*.plist`);
3589
+ const manifestPath = import_path6.default.join(distributionPath, "manifests", `*.plist`);
3070
3590
  return `itms-services://?action=download-manifest&url=file://${manifestPath}`;
3071
3591
  }
3072
3592
  /**
@@ -3075,7 +3595,7 @@ var AdHocUploader = class {
3075
3595
  * @param distributionPath 分发目录
3076
3596
  */
3077
3597
  async generateQRCode(url, distributionPath) {
3078
- const qrCodePath = import_path5.default.join(distributionPath, "install-qrcode.png");
3598
+ const qrCodePath = import_path6.default.join(distributionPath, "install-qrcode.png");
3079
3599
  try {
3080
3600
  logger.info(`QR code would be saved to: ${qrCodePath}`);
3081
3601
  logger.info(`Install URL: ${url}`);
@@ -3102,8 +3622,8 @@ var AdHocUploader = class {
3102
3622
  logger.newLine();
3103
3623
  logger.info("\u{1F4E6} Distribution Files:");
3104
3624
  logger.info(` Directory: ${distributionPath}`);
3105
- logger.info(` IPA: ${import_path5.default.join(distributionPath, "apps", "*.ipa")}`);
3106
- logger.info(` Manifest: ${import_path5.default.join(distributionPath, "manifests", "*.plist")}`);
3625
+ logger.info(` IPA: ${import_path6.default.join(distributionPath, "apps", "*.ipa")}`);
3626
+ logger.info(` Manifest: ${import_path6.default.join(distributionPath, "manifests", "*.plist")}`);
3107
3627
  logger.info(` QR Code: ${qrCodePath}`);
3108
3628
  logger.newLine();
3109
3629
  logger.info("\u{1F517} Installation Link:");
@@ -3162,7 +3682,7 @@ var AdHocUploader = class {
3162
3682
 
3163
3683
  // src/adapters/harmony_uploader.ts
3164
3684
  var import_fs_extra9 = __toESM(require("fs-extra"), 1);
3165
- var import_path6 = __toESM(require("path"), 1);
3685
+ var import_path7 = __toESM(require("path"), 1);
3166
3686
  var import_axios6 = __toESM(require("axios"), 1);
3167
3687
  var import_form_data5 = __toESM(require("form-data"), 1);
3168
3688
  var HarmonyUploader = class _HarmonyUploader {
@@ -3192,7 +3712,7 @@ var HarmonyUploader = class _HarmonyUploader {
3192
3712
  const clientSecret = harmonyConfig.auth.clientSecret;
3193
3713
  const appId = harmonyConfig.appId;
3194
3714
  const releaseType = harmonyConfig.releaseType || "draft";
3195
- const suffix = import_path6.default.extname(filePath).substring(1);
3715
+ const suffix = import_path7.default.extname(filePath).substring(1);
3196
3716
  const retryConfig = config.retry;
3197
3717
  logger.info("Starting HarmonyOS AppGallery upload...");
3198
3718
  try {
@@ -3309,7 +3829,7 @@ var HarmonyUploader = class _HarmonyUploader {
3309
3829
  * @param filePath 文件路径
3310
3830
  */
3311
3831
  async updateAppFileInfo(token, appId, authCode, filePath) {
3312
- const fileName = import_path6.default.basename(filePath);
3832
+ const fileName = import_path7.default.basename(filePath);
3313
3833
  const body = {
3314
3834
  fileType: 7,
3315
3835
  // 鸿蒙包的 fileType(需要根据华为最新文档确认)
@@ -3332,7 +3852,7 @@ var HarmonyUploader = class _HarmonyUploader {
3332
3852
 
3333
3853
  // src/adapters/oppo_uploader.ts
3334
3854
  var import_fs_extra10 = __toESM(require("fs-extra"), 1);
3335
- var import_path7 = __toESM(require("path"), 1);
3855
+ var import_path8 = __toESM(require("path"), 1);
3336
3856
  var import_axios7 = __toESM(require("axios"), 1);
3337
3857
  var import_form_data6 = __toESM(require("form-data"), 1);
3338
3858
  var import_crypto4 = __toESM(require("crypto"), 1);
@@ -3373,7 +3893,7 @@ var OppoUploader = class _OppoUploader {
3373
3893
  * 尝试从文件名推断包名
3374
3894
  */
3375
3895
  inferPackageName(filePath) {
3376
- const fileName = import_path7.default.basename(filePath);
3896
+ const fileName = import_path8.default.basename(filePath);
3377
3897
  const match = fileName.match(/^([\w.]+?)(?:-(?:release|debug|unsigned))?\.apk$/i);
3378
3898
  if (match && match[1].includes(".")) {
3379
3899
  return match[1];
@@ -3391,110 +3911,47 @@ var OppoUploader = class _OppoUploader {
3391
3911
  }
3392
3912
  async upload(filePath, config, metadata) {
3393
3913
  const oppoConfig = config.publish.oppo;
3394
- const clientId = oppoConfig.auth.clientId;
3395
- const clientSecret = oppoConfig.auth.clientSecret;
3914
+ const accessToken = await this.getAccessToken(oppoConfig.auth.clientId, oppoConfig.auth.clientSecret);
3396
3915
  const packageName = oppoConfig.packageName || this.inferPackageName(filePath);
3397
- if (!packageName) {
3398
- return {
3399
- platform: "oppo",
3400
- success: false,
3401
- message: "\u65E0\u6CD5\u83B7\u53D6\u5305\u540D (packageName)\uFF0C\u8BF7\u5728\u914D\u7F6E\u4E2D\u6307\u5B9A\u6216\u786E\u4FDD\u6587\u4EF6\u540D\u5305\u542B\u5305\u540D",
3402
- consoleUrl: "https://open.oppomobile.com/new/ecology/app"
3403
- };
3404
- }
3405
- const retryConfig = config.retry;
3406
3916
  const consoleUrl = "https://open.oppomobile.com/new/ecology/app";
3407
- if (!import_fs_extra10.default.existsSync(filePath)) {
3408
- return {
3409
- platform: "oppo",
3410
- success: false,
3411
- message: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`,
3412
- consoleUrl
3413
- };
3917
+ if (!packageName) {
3918
+ return { platform: "oppo", success: false, message: "\u65E0\u6CD5\u83B7\u53D6\u5305\u540D", consoleUrl };
3414
3919
  }
3415
- logger.info("\u5F00\u59CB\u4E0A\u4F20\u5230 OPPO \u5E94\u7528\u5546\u5E97...");
3416
- logger.info(`\u6587\u4EF6: ${filePath}`);
3920
+ const artifacts = typeof metadata.artifactPath === "object" ? metadata.artifactPath : { universal: filePath || (typeof metadata.artifactPath === "string" ? metadata.artifactPath : "") };
3417
3921
  try {
3418
- const retryStrategy = createUploadRetryStrategy(retryConfig);
3419
- logger.info("\u6B63\u5728\u83B7\u53D6 access_token...");
3420
- const tokenResult = await retryStrategy.execute(
3421
- () => this.getAccessToken(clientId, clientSecret),
3422
- "OPPO authentication"
3423
- );
3424
- if (!tokenResult.success || !tokenResult.data) {
3425
- throw tokenResult.error || new Error("Failed to get access token");
3426
- }
3427
- const accessToken = tokenResult.data;
3428
- logger.info("\u6B63\u5728\u83B7\u53D6\u73B0\u6709\u5E94\u7528\u8BE6\u60C5...");
3429
- const appInfoResult = await retryStrategy.execute(
3430
- () => this.getAppInfo(accessToken, clientSecret, packageName),
3431
- "OPPO get app info"
3432
- );
3433
- const onlineAppInfo = appInfoResult.data || {};
3434
- if (onlineAppInfo) {
3435
- logger.info(
3436
- `\u83B7\u53D6\u5230\u7EBF\u4E0A\u4FE1\u606F: ${onlineAppInfo.app_name} (Category: ${onlineAppInfo.second_category_id})`
3437
- );
3438
- logger.info(`[OPPO AppInfo Full] ${JSON.stringify(onlineAppInfo)}`);
3439
- }
3440
- logger.info("\u6B63\u5728\u83B7\u53D6\u4E0A\u4F20\u5730\u5740...");
3441
- const uploadUrlResult = await retryStrategy.execute(
3442
- () => this.getUploadUrl(accessToken, clientSecret),
3443
- "OPPO get upload URL"
3444
- );
3445
- if (!uploadUrlResult.success || !uploadUrlResult.data) {
3446
- throw uploadUrlResult.error || new Error("Failed to get upload URL");
3447
- }
3448
- const { uploadUrl, sign: uploadSign } = uploadUrlResult.data;
3449
- logger.info("\u6B63\u5728\u4E0A\u4F20 APK \u6587\u4EF6...");
3450
- const uploadFileResult = await retryStrategy.execute(
3451
- () => this.uploadFile(uploadUrl, uploadSign, filePath),
3452
- "OPPO file upload"
3453
- );
3454
- if (!uploadFileResult.success || !uploadFileResult.data) {
3455
- throw uploadFileResult.error || new Error("File upload failed");
3456
- }
3457
- const rawUrl = uploadFileResult.data.url;
3458
- const fileUrl = rawUrl.replace(/^http:\/\//i, "https://").replace("storedl1.nearme.com.cn", "storedl.nearme.com.cn");
3459
- const md5 = uploadFileResult.data.md5;
3460
- logger.info("\u6B63\u5728\u63D0\u4EA4\u7248\u672C\u4FE1\u606F...");
3461
- const submitResult = await retryStrategy.execute(
3462
- () => this.submitVersion({
3463
- accessToken,
3464
- clientSecret,
3465
- packageName,
3466
- appName: oppoConfig.appName || metadata.appName || onlineAppInfo.app_name || this.inferAppName(packageName),
3467
- fileUrl,
3468
- fileMd5: md5,
3469
- versionName: metadata.version,
3470
- versionCode: metadata.buildNumber,
3471
- updateDesc: metadata.changelog || "App Update",
3472
- onlineType: oppoConfig.onlineType,
3473
- // 从配置读取
3474
- onlineAppInfo
3475
- // 传入线上信息
3476
- }),
3477
- "OPPO submit version"
3478
- );
3479
- if (!submitResult.success) {
3480
- throw submitResult.error || new Error("Submit version failed");
3481
- }
3922
+ const retryStrategy = createUploadRetryStrategy(config.retry);
3923
+ const onlineAppInfo = await this.getAppInfo(accessToken, oppoConfig.auth.clientSecret, packageName) || {};
3924
+ const uploadResults = [];
3925
+ const uploadPromises = Object.entries(artifacts).map(async ([abi, p]) => {
3926
+ if (!p || !import_fs_extra10.default.existsSync(p)) throw new Error(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${p}`);
3927
+ logger.info(`[OPPO] \u6B63\u5728\u5904\u7406 ${abi} \u67B6\u6784\u5305...`);
3928
+ const { uploadUrl, sign } = await this.getUploadUrl(accessToken, oppoConfig.auth.clientSecret);
3929
+ const uploadFileResult = await this.uploadFile(uploadUrl, sign, p);
3930
+ const fileUrl = uploadFileResult.url.replace(/^http:\/\//i, "https://").replace("storedl1.nearme.com.cn", "storedl.nearme.com.cn");
3931
+ let cpu_code = 0;
3932
+ if (abi === "arm64-v8a") cpu_code = 3;
3933
+ else if (abi === "armeabi-v7a") cpu_code = 4;
3934
+ uploadResults.push({ url: fileUrl, md5: uploadFileResult.md5, cpu_code });
3935
+ });
3936
+ await Promise.all(uploadPromises);
3937
+ logger.info(`[OPPO] \u6B63\u5728\u63D0\u4EA4\u6D41\u6C34\u53F7\u7EC4: ${JSON.stringify(uploadResults)}`);
3938
+ await this.submitVersion({
3939
+ accessToken,
3940
+ clientSecret: oppoConfig.auth.clientSecret,
3941
+ packageName,
3942
+ appName: oppoConfig.appName || onlineAppInfo.app_name || metadata.appName || this.inferAppName(packageName),
3943
+ apkUrls: uploadResults,
3944
+ versionName: metadata.version,
3945
+ versionCode: metadata.buildNumber,
3946
+ updateDesc: metadata.changelog || "App Update",
3947
+ onlineType: oppoConfig.onlineType,
3948
+ onlineAppInfo
3949
+ });
3482
3950
  logger.success("\u4E0A\u4F20\u5230 OPPO \u5E94\u7528\u5546\u5E97\u6210\u529F!");
3483
- return {
3484
- platform: "oppo",
3485
- success: true,
3486
- message: "\u4E0A\u4F20\u6210\u529F, \u8BF7\u524D\u5F80\u540E\u53F0\u786E\u8BA4\u5E76\u63D0\u4EA4\u5BA1\u6838",
3487
- consoleUrl
3488
- };
3951
+ return { platform: "oppo", success: true, message: "\u4E0A\u4F20\u6210\u529F, \u8BF7\u786E\u8BA4", consoleUrl };
3489
3952
  } catch (error) {
3490
3953
  logger.error(`\u4E0A\u4F20\u5230 OPPO \u5931\u8D25: ${error.message}`);
3491
- return {
3492
- platform: "oppo",
3493
- success: false,
3494
- message: this.formatError(error),
3495
- error,
3496
- consoleUrl
3497
- };
3954
+ return { platform: "oppo", success: false, message: error.message, error, consoleUrl };
3498
3955
  }
3499
3956
  }
3500
3957
  /**
@@ -3615,16 +4072,7 @@ var OppoUploader = class _OppoUploader {
3615
4072
  const signParams = {
3616
4073
  access_token: params.accessToken,
3617
4074
  timestamp,
3618
- // OPPO 官方要求 apk_url 为 JSON 数组格式:[{url, md5, cpu_code}]
3619
- // cpu_code: 0=通用包, 3=arm64-v8a, 4=armeabi-v7a, 5=x86_64
3620
- apk_url: JSON.stringify([
3621
- {
3622
- url: params.fileUrl,
3623
- md5: params.fileMd5,
3624
- cpu_code: 0
3625
- // 通用包
3626
- }
3627
- ]),
4075
+ apk_url: JSON.stringify(params.apkUrls),
3628
4076
  version_name: params.versionName.split("+")[0],
3629
4077
  // 仅保留版本名部分
3630
4078
  version_code: vCode,
@@ -3726,7 +4174,7 @@ var OppoUploader = class _OppoUploader {
3726
4174
 
3727
4175
  // src/adapters/vivo_uploader.ts
3728
4176
  var import_fs_extra11 = __toESM(require("fs-extra"), 1);
3729
- var import_path8 = __toESM(require("path"), 1);
4177
+ var import_path9 = __toESM(require("path"), 1);
3730
4178
  var import_axios8 = __toESM(require("axios"), 1);
3731
4179
  var import_form_data7 = __toESM(require("form-data"), 1);
3732
4180
  var import_crypto5 = __toESM(require("crypto"), 1);
@@ -3734,26 +4182,14 @@ var VivoUploader = class _VivoUploader {
3734
4182
  name = "vivo";
3735
4183
  /** vivo 开放 API 基础地址 */
3736
4184
  static API_BASE = "https://developer-api.vivo.com.cn/router/rest";
3737
- /**
3738
- * vivo API 签名生成
3739
- * 规则: 所有参数按 ASCII 码排序后拼接为 key=val&... 字符串,
3740
- * 以 accessSecret 为密钥进行 HMAC-SHA256 加密,结果转为小写 16 进制字符串
3741
- */
3742
4185
  generateSign(params, accessSecret) {
3743
4186
  const sorted = Object.keys(params).sort().filter((key) => params[key] !== void 0 && params[key] !== null).map((key) => `${key}=${params[key]}`).join("&");
3744
- const sign = import_crypto5.default.createHmac("sha256", accessSecret).update(sorted).digest("hex");
3745
- logger.info(`[VIVO DEBUG] \u5F85\u7B7E\u540D\u4E32: ${sorted}
3746
- -> \u7B7E\u540D\u7ED3\u679C: ${sign}`);
3747
- return sign;
4187
+ return import_crypto5.default.createHmac("sha256", accessSecret).update(sorted).digest("hex");
3748
4188
  }
3749
4189
  validateConfig(config) {
3750
4190
  if (!config?.publish?.vivo?.enable) return false;
3751
4191
  const vivo = config.publish.vivo;
3752
- if (!vivo.auth?.accessKey || !vivo.auth?.accessSecret) {
3753
- logger.error("vivo config missing: accessKey or accessSecret");
3754
- return false;
3755
- }
3756
- return true;
4192
+ return !!(vivo.auth?.accessKey && vivo.auth?.accessSecret);
3757
4193
  }
3758
4194
  canHandle(filePath) {
3759
4195
  const ext = filePath.split(".").pop()?.toLowerCase();
@@ -3763,67 +4199,51 @@ var VivoUploader = class _VivoUploader {
3763
4199
  const vivoConfig = config.publish.vivo;
3764
4200
  const accessKey = vivoConfig.auth.accessKey;
3765
4201
  const accessSecret = vivoConfig.auth.accessSecret;
3766
- const retryConfig = config.retry;
3767
4202
  const consoleUrl = "https://dev.vivo.com.cn/manage/app";
3768
- if (!import_fs_extra11.default.existsSync(filePath)) {
3769
- return {
3770
- platform: "vivo",
3771
- success: false,
3772
- message: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`,
3773
- consoleUrl
3774
- };
3775
- }
3776
- const fileSize = import_fs_extra11.default.statSync(filePath).size;
3777
- logger.info("\u5F00\u59CB\u4E0A\u4F20\u5230 vivo \u5E94\u7528\u5546\u5E97...");
3778
- logger.info(`\u6587\u4EF6: ${filePath} (${(fileSize / 1024 / 1024).toFixed(2)} MB)`);
4203
+ const artifacts = typeof metadata.artifactPath === "object" ? metadata.artifactPath : { universal: filePath || (typeof metadata.artifactPath === "string" ? metadata.artifactPath : "") };
3779
4204
  try {
3780
- const retryStrategy = createUploadRetryStrategy(retryConfig);
3781
- const fileMd5 = await this.calculateFileMD5(filePath);
3782
- logger.info("\u6B63\u5728\u4E0A\u4F20 APK \u6587\u4EF6...");
3783
- const packageName = vivoConfig.packageName || "";
3784
- const uploadFileResult = await retryStrategy.execute(
3785
- () => this.uploadApk(accessKey, accessSecret, filePath, packageName, fileMd5),
3786
- "vivo file upload"
3787
- );
3788
- if (!uploadFileResult.success || !uploadFileResult.data) {
3789
- throw uploadFileResult.error || new Error("File upload failed or no serialnumber returned");
4205
+ const uploadResults = {};
4206
+ const uploadPromises = Object.entries(artifacts).map(async ([abi, p]) => {
4207
+ if (!p || !import_fs_extra11.default.existsSync(p)) throw new Error(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${p}`);
4208
+ const md5 = await this.calculateFileMD5(p);
4209
+ logger.info(`[vivo] \u6B63\u5728\u5904\u7406 ${abi} \u67B6\u6784\u5305: ${import_path9.default.basename(p)}`);
4210
+ const serialnumber = await this.uploadApk(accessKey, accessSecret, p, vivoConfig.packageName || "", md5);
4211
+ const pkgInfo = await parsePackageInfo(p, { androidSdkPath: config.globalSettings?.androidSdkPath });
4212
+ const nativeCodes = pkgInfo?.nativeCode || [];
4213
+ const has64Bit = nativeCodes.includes("arm64-v8a") || nativeCodes.includes("x86_64") || abi === "arm64-v8a";
4214
+ const has32Bit = nativeCodes.includes("armeabi-v7a") || nativeCodes.includes("armeabi") || abi === "armeabi-v7a";
4215
+ uploadResults[abi] = { serialnumber, md5, is64Bit: has64Bit && !has32Bit };
4216
+ });
4217
+ await Promise.all(uploadPromises);
4218
+ const arm64Result = uploadResults["arm64-v8a"] || Object.values(uploadResults).find((r) => r.is64Bit);
4219
+ const arm32Result = uploadResults["armeabi-v7a"] || Object.values(uploadResults).find((r) => !r.is64Bit);
4220
+ const submitData = {
4221
+ packageName: vivoConfig.packageName,
4222
+ versionCode: parseInt(metadata.buildNumber) || 1,
4223
+ updateDesc: metadata.changelog || "App Update"
4224
+ };
4225
+ if (arm64Result) {
4226
+ submitData.serialnumber = arm64Result.serialnumber;
4227
+ submitData.fileMd5 = arm64Result.md5;
3790
4228
  }
3791
- const serialnumber = uploadFileResult.data;
3792
- logger.info("\u6B63\u5728\u63D0\u4EA4\u7248\u672C\u4FE1\u606F...");
3793
- const submitResult = await retryStrategy.execute(
3794
- () => this.submitVersion(accessKey, accessSecret, {
3795
- serialnumber,
3796
- packageName: vivoConfig.packageName,
3797
- versionCode: parseInt(metadata.buildNumber) || 1,
3798
- fileMd5,
3799
- updateDesc: metadata.changelog || "App Update"
3800
- }),
3801
- "vivo submit version"
3802
- );
3803
- if (!submitResult.success) {
3804
- throw submitResult.error || new Error("Submit version failed");
4229
+ if (arm32Result) {
4230
+ submitData.apk32 = arm32Result.serialnumber;
4231
+ submitData.md532 = arm32Result.md5;
3805
4232
  }
4233
+ if (!submitData.serialnumber && !submitData.apk32) {
4234
+ const single = Object.values(uploadResults)[0];
4235
+ submitData.serialnumber = single.serialnumber;
4236
+ submitData.fileMd5 = single.md5;
4237
+ }
4238
+ logger.info(`[vivo] \u6B63\u5728\u63D0\u4EA4\u5BA1\u6838\u7533\u62A5: ${JSON.stringify(submitData)}`);
4239
+ await this.submitVersion(accessKey, accessSecret, submitData);
3806
4240
  logger.success("\u4E0A\u4F20\u5230 vivo \u5E94\u7528\u5546\u5E97\u6210\u529F!");
3807
- return {
3808
- platform: "vivo",
3809
- success: true,
3810
- message: "\u4E0A\u4F20\u6210\u529F, \u8BF7\u524D\u5F80\u540E\u53F0\u786E\u8BA4\u5E76\u63D0\u4EA4\u5BA1\u6838",
3811
- consoleUrl
3812
- };
4241
+ return { platform: "vivo", success: true, message: "\u4E0A\u4F20\u5E76\u63D0\u4EA4\u5BA1\u6838\u6210\u529F", consoleUrl };
3813
4242
  } catch (error) {
3814
4243
  logger.error(`\u4E0A\u4F20\u5230 vivo \u5931\u8D25: ${error.message}`);
3815
- return {
3816
- platform: "vivo",
3817
- success: false,
3818
- message: this.formatError(error),
3819
- error,
3820
- consoleUrl
3821
- };
4244
+ return { platform: "vivo", success: false, message: error.message, error, consoleUrl };
3822
4245
  }
3823
4246
  }
3824
- /**
3825
- * 计算文件 MD5
3826
- */
3827
4247
  calculateFileMD5(filePath) {
3828
4248
  return new Promise((resolve, reject) => {
3829
4249
  const hash = import_crypto5.default.createHash("md5");
@@ -3833,14 +4253,9 @@ var VivoUploader = class _VivoUploader {
3833
4253
  stream.on("error", reject);
3834
4254
  });
3835
4255
  }
3836
- /**
3837
- * 上传 APK 文件并获取流水号
3838
- * 将公共参数与业务参数放入一个 multipart 请求中直接上传至 API_BASE
3839
- */
3840
4256
  async uploadApk(accessKey, accessSecret, filePath, packageName, fileMd5) {
3841
4257
  const timestamp = String(Date.now());
3842
- const paramsForSign = {
3843
- // 公共参数
4258
+ const params = {
3844
4259
  access_key: accessKey,
3845
4260
  timestamp,
3846
4261
  format: "json",
@@ -3848,41 +4263,19 @@ var VivoUploader = class _VivoUploader {
3848
4263
  sign_method: "hmac",
3849
4264
  target_app_key: "developer",
3850
4265
  method: "app.upload.apk.app",
3851
- // 业务参数
3852
4266
  packageName,
3853
4267
  fileMd5
3854
4268
  };
3855
- const sign = this.generateSign(paramsForSign, accessSecret);
4269
+ const sign = this.generateSign(params, accessSecret);
3856
4270
  const form = new import_form_data7.default();
3857
- for (const [key, value] of Object.entries(paramsForSign)) {
3858
- form.append(key, value);
3859
- }
4271
+ Object.entries(params).forEach(([k, v]) => form.append(k, v));
3860
4272
  form.append("sign", sign);
3861
- form.append("file", import_fs_extra11.default.createReadStream(filePath), {
3862
- filename: import_path8.default.basename(filePath),
3863
- contentType: "application/vnd.android.package-archive"
3864
- });
3865
- logger.info(
3866
- `[VIVO DEBUG] \u5F00\u59CB\u4E0A\u4F20 APK (API\u8BF7\u6C42) \u53C2\u6570(\u4E0D\u542B\u6587\u4EF6): ${JSON.stringify({ ...paramsForSign, sign })}`
3867
- );
3868
- const res = await import_axios8.default.post(_VivoUploader.API_BASE, form, {
3869
- headers: form.getHeaders(),
3870
- maxContentLength: Infinity,
3871
- maxBodyLength: Infinity,
3872
- timeout: 3e5
3873
- });
3874
- logger.info(`[VIVO DEBUG] \u4E0A\u4F20\u54CD\u5E94: ${JSON.stringify(res.data)}`);
3875
- if (res.data.code !== 0) {
3876
- throw new Error(`\u6587\u4EF6\u4E0A\u4F20\u5931\u8D25: ${res.data.msg || JSON.stringify(res.data)}`);
3877
- }
3878
- const serialnumber = res.data.data?.serialnumber || res.data.data;
3879
- logger.info(`APK \u6587\u4EF6\u4E0A\u4F20\u5B8C\u6210\uFF0C\u6D41\u6C34\u53F7: ${serialnumber}`);
3880
- return serialnumber;
4273
+ form.append("file", import_fs_extra11.default.createReadStream(filePath), { filename: import_path9.default.basename(filePath) });
4274
+ const res = await import_axios8.default.post(_VivoUploader.API_BASE, form, { headers: form.getHeaders(), timeout: 3e5 });
4275
+ if (res.data.code !== 0) throw new Error(`\u4E0A\u4F20\u5305\u5931\u8D25: ${res.data.msg}`);
4276
+ return res.data.data?.serialnumber || res.data.data;
3881
4277
  }
3882
- /**
3883
- * 提交版本更新信息
3884
- */
3885
- async submitVersion(accessKey, accessSecret, params) {
4278
+ async submitVersion(accessKey, accessSecret, data) {
3886
4279
  const timestamp = String(Date.now());
3887
4280
  const reqParams = {
3888
4281
  access_key: accessKey,
@@ -3892,51 +4285,32 @@ var VivoUploader = class _VivoUploader {
3892
4285
  sign_method: "hmac",
3893
4286
  target_app_key: "developer",
3894
4287
  method: "app.sync.update.app",
3895
- packageName: params.packageName || "",
3896
- // 文档必填
3897
- versionCode: String(params.versionCode),
3898
- // 文档必填 (Integer)
3899
- apk: params.serialnumber,
3900
- // 文档中的 apk 字段即为上传接口返回的 serialnumber
3901
- fileMd5: params.fileMd5,
3902
- // 文档必填
4288
+ packageName: data.packageName || "",
4289
+ versionCode: String(data.versionCode),
4290
+ apk: data.serialnumber || "",
4291
+ fileMd5: data.fileMd5 || "",
3903
4292
  onlineType: "1",
3904
- // 1:立即上架 2:定时上架 (必填)
3905
4293
  compatibleDevice: "1",
3906
- // 1:手机 2:手机和平板 3:平板 (必填)
3907
- updateDesc: params.updateDesc
4294
+ updateDesc: data.updateDesc
3908
4295
  };
4296
+ if (data.apk32) {
4297
+ reqParams.apk32 = data.apk32;
4298
+ if (data.md532) reqParams.md532 = data.md532;
4299
+ }
3909
4300
  reqParams.sign = this.generateSign(reqParams, accessSecret);
3910
4301
  const formData = new URLSearchParams();
3911
- for (const [key, value] of Object.entries(reqParams)) {
3912
- formData.append(key, value);
3913
- }
4302
+ Object.entries(reqParams).forEach(([k, v]) => formData.append(k, v));
3914
4303
  const res = await import_axios8.default.post(_VivoUploader.API_BASE, formData.toString(), {
3915
- headers: {
3916
- "Content-Type": "application/x-www-form-urlencoded"
3917
- },
4304
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
3918
4305
  timeout: 3e4
3919
4306
  });
3920
- if (res.data.code !== 0) {
3921
- throw new Error(`\u63D0\u4EA4\u7248\u672C\u5931\u8D25: ${res.data.msg || JSON.stringify(res.data)}`);
3922
- }
3923
- logger.info("\u7248\u672C\u4FE1\u606F\u5DF2\u63D0\u4EA4");
3924
- }
3925
- /**
3926
- * 格式化错误信息
3927
- */
3928
- formatError(error) {
3929
- if (error.response?.data) {
3930
- const data = error.response.data;
3931
- return data.msg || data.message || error.message;
3932
- }
3933
- return error.message;
4307
+ if (res.data.code !== 0) throw new Error(`\u63D0\u4EA4\u5931\u8D25: ${res.data.msg}`);
3934
4308
  }
3935
4309
  };
3936
4310
 
3937
4311
  // src/adapters/tencent_uploader.ts
3938
4312
  var import_fs_extra12 = __toESM(require("fs-extra"), 1);
3939
- var import_path9 = __toESM(require("path"), 1);
4313
+ var import_path10 = __toESM(require("path"), 1);
3940
4314
  var import_axios9 = __toESM(require("axios"), 1);
3941
4315
  var import_crypto6 = __toESM(require("crypto"), 1);
3942
4316
  var TencentUploader = class _TencentUploader {
@@ -3968,54 +4342,71 @@ var TencentUploader = class _TencentUploader {
3968
4342
  async upload(filePath, config, metadata) {
3969
4343
  const tencentConfig = config.publish.tencent;
3970
4344
  const auth = tencentConfig.auth;
3971
- logger.info(`Tencent \u8FD0\u884C\u65F6\u914D\u7F6E: ${JSON.stringify(tencentConfig)}`);
3972
4345
  const consoleUrl = "https://app.open.qq.com";
3973
- if (!import_fs_extra12.default.existsSync(filePath)) {
3974
- return { platform: "tencent", success: false, message: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`, consoleUrl };
3975
- }
4346
+ const pkgName = tencentConfig.packageName || "";
4347
+ const artifacts = typeof metadata.artifactPath === "object" ? metadata.artifactPath : { universal: filePath || (typeof metadata.artifactPath === "string" ? metadata.artifactPath : "") };
3976
4348
  try {
3977
- const stats = import_fs_extra12.default.statSync(filePath);
3978
- const fileName = import_path9.default.basename(filePath);
3979
- logger.info("\u6B63\u5728\u83B7\u53D6\u9884\u7B7E\u540D\u4E0A\u4F20\u5730\u5740...");
3980
- const pkgName = tencentConfig.packageName || "";
3981
- const uploadInfo = await this.getFileUploadInfo(auth, stats.size, fileName, pkgName);
3982
- logger.info(`\u83B7\u53D6\u6587\u4EF6\u4E0A\u4F20\u4FE1\u606F\u7ED3\u679C ${JSON.stringify(uploadInfo)}`);
3983
- logger.info("\u6B63\u5728\u4E0A\u4F20\u6587\u4EF6\u5230\u817E\u8BAF\u4E91 COS (\u7ED5\u8FC7 API \u7F51\u5173\u9650\u5236)...");
3984
- await this.uploadToCos(uploadInfo.pre_sign_url, filePath, stats.size);
3985
- logger.info("\u6B63\u5728\u83B7\u53D6\u7EBF\u4E0A\u5E94\u7528\u8BE6\u60C5\u4EE5\u4FDD\u7559\u73B0\u6709\u914D\u7F6E...");
3986
- const pkgInfo = await parsePackageInfo(filePath, {
4349
+ const uploadResults = {};
4350
+ const uploadPromises = Object.entries(artifacts).map(async ([abi, p]) => {
4351
+ if (!p || !import_fs_extra12.default.existsSync(p)) throw new Error(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${p}`);
4352
+ const stats = import_fs_extra12.default.statSync(p);
4353
+ const fileName = import_path10.default.basename(p);
4354
+ logger.info(`[Tencent] \u6B63\u5728\u5904\u7406 ${abi} \u67B6\u6784\u5305: ${fileName}`);
4355
+ const uploadInfo = await this.getFileUploadInfo(auth, stats.size, fileName, pkgName);
4356
+ await this.uploadToCos(uploadInfo.pre_sign_url, p, stats.size);
4357
+ const pkgInfo = await parsePackageInfo(p, {
4358
+ androidSdkPath: config.globalSettings?.androidSdkPath
4359
+ });
4360
+ const nativeCodes = pkgInfo?.nativeCode || [];
4361
+ const has64Bit = nativeCodes.includes("arm64-v8a") || nativeCodes.includes("x86_64") || abi === "arm64-v8a";
4362
+ const has32Bit = nativeCodes.includes("armeabi-v7a") || nativeCodes.includes("armeabi") || nativeCodes.includes("x86") || abi === "armeabi-v7a";
4363
+ uploadResults[abi] = {
4364
+ serial_number: uploadInfo.serial_number,
4365
+ is64Bit: has64Bit && !has32Bit,
4366
+ md5: pkgInfo?.md5 || ""
4367
+ };
4368
+ });
4369
+ await Promise.all(uploadPromises);
4370
+ logger.info("[Tencent] \u6B63\u5728\u83B7\u53D6\u7EBF\u4E0A\u5E94\u7528\u8BE6\u60C5...");
4371
+ const anyPkgPath = Object.values(artifacts)[0];
4372
+ const samplePkgInfo = await parsePackageInfo(anyPkgPath, {
3987
4373
  androidSdkPath: config.globalSettings?.androidSdkPath
3988
4374
  });
3989
- const nativeCodes = pkgInfo?.nativeCode || [];
3990
- const has64Bit = nativeCodes.includes("arm64-v8a") || nativeCodes.includes("x86_64");
3991
- const has32Bit = nativeCodes.includes("armeabi-v7a") || nativeCodes.includes("armeabi") || nativeCodes.includes("x86");
3992
- const is64Bit = has64Bit && !has32Bit;
3993
- const md5 = pkgInfo?.md5;
3994
4375
  const onlineDetail = await this.queryAppDetail(
3995
4376
  auth,
3996
- tencentConfig.packageName || pkgInfo?.packageName || ""
3997
- );
3998
- logger.info(`\u83B7\u53D6\u7EBF\u4E0A\u8BE6\u60C5\u6210\u529F: ${onlineDetail.app_name || "\u672A\u547D\u540D"}`);
3999
- logger.info("\u6B63\u5728\u63D0\u4EA4\u6D41\u6C34\u53F7\u5B8C\u6210\u5BA1\u6838\u7533\u62A5...");
4000
- const submitResult = await this.submitModifyAppInfo(
4001
- auth,
4002
- {
4003
- ...onlineDetail,
4004
- // 以前序详情为蓝本
4005
- pkg_name: tencentConfig.packageName || pkgInfo?.packageName || onlineDetail.pkg_name || "",
4006
- serial_number: uploadInfo.serial_number,
4007
- update_desc: metadata.changelog || "\u7248\u672C\u66F4\u65B0",
4008
- is64Bit,
4009
- md5
4010
- },
4011
- { timeout: tencentConfig.timeout || 6e4 }
4377
+ pkgName || samplePkgInfo?.packageName || ""
4012
4378
  );
4013
- logger.info(`\u63D0\u4EA4\u6D41\u6C34\u53F7\u5B8C\u6210\u5BA1\u6838\u7533\u62A5\u7ED3\u679C: ${JSON.stringify(submitResult)}`);
4379
+ const submitData = {
4380
+ ...onlineDetail,
4381
+ pkg_name: pkgName || samplePkgInfo?.packageName || onlineDetail.pkg_name || "",
4382
+ update_desc: metadata.changelog || "\u7248\u672C\u66F4\u65B0"
4383
+ };
4384
+ const arm64Result = uploadResults["arm64-v8a"] || Object.values(uploadResults).find((r) => r.is64Bit);
4385
+ const arm32Result = uploadResults["armeabi-v7a"] || Object.values(uploadResults).find((r) => !r.is64Bit);
4386
+ if (arm64Result) {
4387
+ submitData.apk64_serial_number = arm64Result.serial_number;
4388
+ submitData.apk64_md5 = arm64Result.md5;
4389
+ }
4390
+ if (arm32Result) {
4391
+ submitData.apk32_serial_number = arm32Result.serial_number;
4392
+ submitData.apk32_md5 = arm32Result.md5;
4393
+ }
4394
+ if (Object.keys(uploadResults).length === 1 && !submitData.apk64_serial_number && !submitData.apk32_serial_number) {
4395
+ const single = Object.values(uploadResults)[0];
4396
+ if (single.is64Bit) {
4397
+ submitData.apk64_serial_number = single.serial_number;
4398
+ submitData.apk64_md5 = single.md5;
4399
+ } else {
4400
+ submitData.apk32_serial_number = single.serial_number;
4401
+ submitData.apk32_md5 = single.md5;
4402
+ }
4403
+ }
4404
+ logger.info("[Tencent] \u6B63\u5728\u63D0\u4EA4\u5BA1\u6838\u7533\u62A5...");
4405
+ await this.submitModifyAppInfo(auth, submitData, { timeout: tencentConfig.timeout || 6e4 });
4014
4406
  logger.success("\u817E\u8BAF\u5E94\u7528\u5B9D\u4E0A\u4F20\u5E76\u63D0\u4EA4\u6210\u529F!");
4015
4407
  return { platform: "tencent", success: true, message: "\u4E0A\u4F20\u5E76\u63D0\u4EA4\u5BA1\u6838\u6210\u529F", consoleUrl };
4016
4408
  } catch (error) {
4017
- logger.error(`\u817E\u8BAF\u4E0A\u4F20\u5931\u8D25: ${JSON.stringify(error)}`);
4018
- logger.error(`\u817E\u8BAF\u4E0A\u4F20\u5931\u8D25: ${error.stack}`);
4409
+ logger.error(`\u817E\u8BAF\u4E0A\u4F20\u5931\u8D25: ${error.message}`);
4019
4410
  return { platform: "tencent", success: false, message: error.message, error, consoleUrl };
4020
4411
  }
4021
4412
  }
@@ -4129,16 +4520,22 @@ var TencentUploader = class _TencentUploader {
4129
4520
  signParams[key] = String(data[key]);
4130
4521
  }
4131
4522
  });
4132
- if (data.is64Bit) {
4133
- signParams.apk64_file_serial_number = String(data.serial_number || "");
4134
- if (data.md5) signParams.apk64_file_md5 = data.md5;
4135
- delete signParams.apk32_file_serial_number;
4136
- delete signParams.apk32_file_md5;
4137
- } else {
4138
- signParams.apk32_file_serial_number = String(data.serial_number || "");
4139
- if (data.md5) signParams.apk32_file_md5 = data.md5;
4140
- delete signParams.apk64_file_serial_number;
4141
- delete signParams.apk64_file_md5;
4523
+ if (data.apk64_serial_number) {
4524
+ signParams.apk64_file_serial_number = String(data.apk64_serial_number);
4525
+ if (data.apk64_md5) signParams.apk64_file_md5 = data.apk64_md5;
4526
+ }
4527
+ if (data.apk32_serial_number) {
4528
+ signParams.apk32_file_serial_number = String(data.apk32_serial_number);
4529
+ if (data.apk32_md5) signParams.apk32_file_md5 = data.apk32_md5;
4530
+ }
4531
+ if (!data.apk64_serial_number && !data.apk32_serial_number && data.serial_number) {
4532
+ if (data.is64Bit) {
4533
+ signParams.apk64_file_serial_number = String(data.serial_number || "");
4534
+ if (data.md5) signParams.apk64_file_md5 = data.md5;
4535
+ } else {
4536
+ signParams.apk32_file_serial_number = String(data.serial_number || "");
4537
+ if (data.md5) signParams.apk32_file_md5 = data.md5;
4538
+ }
4142
4539
  }
4143
4540
  const sign = this.generateSign(signParams, accessSecret);
4144
4541
  const formParams = {