@ttmg/cli 0.3.1 → 0.3.2-beta.3

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.js CHANGED
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
+ var os = require('os');
5
+ var path = require('path');
6
+ var fs = require('fs');
4
7
  var commander = require('commander');
5
8
  var inquirer = require('inquirer');
6
- var fs = require('fs');
7
9
  var jsdom = require('jsdom');
8
10
  var prettier = require('prettier');
9
11
  var chalk = require('chalk');
10
12
  var express = require('express');
11
- var path = require('path');
12
13
  var cheerio = require('cheerio');
13
- var os = require('os');
14
14
  var worker_threads = require('worker_threads');
15
15
  var axios = require('axios');
16
16
  var qs = require('qs');
@@ -24,6 +24,7 @@ var got = require('got');
24
24
  var FormData$1 = require('form-data');
25
25
  var stream = require('stream');
26
26
  var ttmgPack = require('ttmg-pack');
27
+ var boxen = require('boxen');
27
28
  var http = require('http');
28
29
  var fs$1 = require('node:fs');
29
30
  var path$1 = require('node:path');
@@ -49,9 +50,9 @@ function _interopNamespaceDefault(e) {
49
50
  return Object.freeze(n);
50
51
  }
51
52
 
52
- var fs__namespace = /*#__PURE__*/_interopNamespaceDefault(fs);
53
- var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
54
53
  var os__namespace = /*#__PURE__*/_interopNamespaceDefault(os);
54
+ var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
55
+ var fs__namespace = /*#__PURE__*/_interopNamespaceDefault(fs);
55
56
  var glob__namespace = /*#__PURE__*/_interopNamespaceDefault(glob);
56
57
 
57
58
  const CONFIG_FILE_NAME = 'minigame.config.json';
@@ -254,7 +255,7 @@ async function checkUpdate() {
254
255
  });
255
256
  }
256
257
 
257
- const CONFIG_PATH = path.join(os.homedir(), '.ttmgrc');
258
+ const CONFIG_PATH = process.env.TTMGRC_PATH || path.join(os.homedir(), '.ttmgrc');
258
259
  const getTTMGRC = () => {
259
260
  // only check one time
260
261
  if (!fs.existsSync(CONFIG_PATH)) {
@@ -281,7 +282,15 @@ const params = {
281
282
  sdk_version: '2.1.6-tiktok',
282
283
  };
283
284
  const prompt = inquirer.createPromptModule();
284
- async function login() {
285
+ async function login(options) {
286
+ const verbose = options?.verbose === true;
287
+ const log = (msg, data) => {
288
+ if (!verbose)
289
+ return;
290
+ console.log(chalk.gray('[ttmg login]'), msg, data !== undefined ? data : '');
291
+ };
292
+ if (verbose)
293
+ log('Verbose logging enabled');
285
294
  console.log(chalk.yellowBright('⚠️ Note: Please login with your TikTok Developer Platform account.'));
286
295
  // 增加对邮箱密码的校验
287
296
  const { email, password } = await prompt([
@@ -318,6 +327,8 @@ async function login() {
318
327
  },
319
328
  ]);
320
329
  const url = LOGIN_TT4D + '?' + new URLSearchParams(params);
330
+ log('Request URL', url);
331
+ log('Request params', { ...params, email: email.replace(/(.{2}).*(@.*)/, '$1***$2') });
321
332
  const headers = {
322
333
  'Content-Type': 'application/x-www-form-urlencoded',
323
334
  Accept: '*/*',
@@ -339,20 +350,25 @@ async function login() {
339
350
  fixed_mix_mode: '1',
340
351
  });
341
352
  try {
353
+ log('Sending POST request...');
342
354
  const response = await axios.post(url, data, {
343
355
  headers,
344
356
  maxRedirects: 20,
345
357
  timeout: 30000,
346
358
  });
359
+ log('Response status', response.status);
360
+ log('Response data', response?.data);
347
361
  if (!response?.data?.data?.user_id) {
348
362
  const errCode = response.data?.data?.error_code;
349
363
  const errMsg = response.data?.data?.description;
350
364
  if (errCode || errMsg) {
365
+ log('Login failed (api)', { errCode, errMsg, fullData: response?.data });
351
366
  spinner$1.fail(chalk.red(`login failed: ${errMsg}${errCode ? `, error_code: ${errCode}` : ''}`));
352
367
  }
353
368
  else {
369
+ log('Login failed (no user_id)', { fullResponse: response?.data });
354
370
  spinner$1.fail(chalk.red(`login failed`));
355
- console.log(response.data);
371
+ log('Login failed', { fullResponse: response });
356
372
  }
357
373
  spinner$1.stop();
358
374
  process.exit(1);
@@ -363,12 +379,14 @@ async function login() {
363
379
  user_id: response?.data?.data?.user_id,
364
380
  cookie: response?.headers['set-cookie'].join('; '),
365
381
  };
382
+ log('Login success', { user_id: data.user_id, email: data.email });
366
383
  setTTMGRC(data);
367
384
  spinner$1.succeed(chalk.bold.green('login successfully!'));
368
385
  process.exit(0);
369
386
  }
370
387
  }
371
388
  catch (error) {
389
+ log('Request error', error instanceof Error ? { message: error.message, name: error.name, stack: error.stack } : error);
372
390
  // 1. Error Title: Red and bold
373
391
  spinner$1.fail(chalk.red.bold('Failed to connect to login service'));
374
392
  // 2. Description: Normal text with bold keywords
@@ -380,6 +398,25 @@ async function login() {
380
398
  }
381
399
  }
382
400
 
401
+ async function setup(options) {
402
+ const inputLang = options?.lang;
403
+ const normalizedLang = inputLang === 'en-US' || inputLang === 'zh-CN' ? inputLang : null;
404
+ const lang = normalizedLang ??
405
+ (await inquirer.createPromptModule()([
406
+ {
407
+ type: 'list',
408
+ name: 'lang',
409
+ message: 'Select language your prefer',
410
+ choices: [
411
+ { name: 'English (en-US)', value: 'en-US' },
412
+ { name: '简体中文 (zh-CN)', value: 'zh-CN' },
413
+ ],
414
+ default: 'en-US',
415
+ },
416
+ ])).lang;
417
+ setTTMGRC({ lang });
418
+ }
419
+
383
420
  // ppe_dev_tool
384
421
  async function request({ url, method, data, headers, params, }) {
385
422
  const config = getTTMGRC();
@@ -475,115 +512,62 @@ function isAxiosError(e) {
475
512
  }
476
513
 
477
514
  async function fetchGameInfo(clientKey) {
478
- if (clientKey?.startsWith('sb')) {
479
- return await fetchGameInfoV3(clientKey);
515
+ // 访问 V4 接口
516
+ const response = await request({
517
+ url: `https://developers.tiktok.com/tiktok/v4/devportal/mini_game/devtool/info`,
518
+ method: 'GET',
519
+ params: {
520
+ client_key: clientKey,
521
+ },
522
+ });
523
+ if (!response?.data) {
524
+ return {
525
+ error: {
526
+ errorCode: response?.error?.StatusCode,
527
+ errorMsg: response?.error?.StatusMessage,
528
+ clientKey: clientKey,
529
+ },
530
+ data: null,
531
+ };
480
532
  }
481
533
  else {
482
- return await fetchGameInfoV4(clientKey);
483
- }
484
- async function fetchGameInfoV3(clientKey) {
485
- const response = await request({
486
- url: `https://developers.tiktok.com/tiktok/v3/devportal/minigame/devtool/sandbox/get`,
487
- // url: `https://developers.tiktok.com/tiktok/v3/devportal/minigame/info`,
488
- method: 'GET',
489
- params: {
490
- client_key: clientKey,
491
- // version_type: 1,
534
+ return {
535
+ error: null,
536
+ data: {
537
+ app_id: response?.data?.mini_game_id,
538
+ name: response?.data?.mini_game_name,
539
+ description: response?.data?.description,
540
+ client_key: response?.data?.client_key,
541
+ request_allowed_domains: response?.data?.trusted_domains,
542
+ display_icon_url: response?.data?.icon_url,
543
+ privacy_policy_link: response?.data?.privacy_policy,
544
+ terms_of_service_link: response?.data?.terms_of_service,
492
545
  },
493
- });
494
- const gameInfo = response?.data?.mini_game;
495
- if (!gameInfo) {
496
- return {
497
- error: {
498
- errorCode: response?.error?.StatusCode,
499
- errorMsg: response?.error?.StatusMessage,
500
- clientKey: clientKey,
501
- client_key: clientKey,
502
- },
503
- data: null,
504
- };
505
- }
506
- else {
507
- return {
508
- error: null,
509
- data: {
510
- ...gameInfo,
511
- },
512
- };
513
- }
514
- }
515
- async function fetchGameInfoV4(clientKey) {
516
- // 访问 V4 接口
517
- const response = await request({
518
- url: `https://developers.tiktok.com/tiktok/v4/devportal/mini_game/devtool/info`,
519
- method: 'GET',
520
- params: {
521
- client_key: clientKey,
522
- // version_type: 1,
523
- }
524
- // headers: {
525
- // 'x-use-ppe': '1',
526
- // 'x-tt-env': 'ppe_mg_revamp',
527
- // },
528
- });
529
- if (!response?.data) {
530
- return {
531
- error: {
532
- errorCode: response?.error?.StatusCode,
533
- errorMsg: response?.error?.StatusMessage,
534
- clientKey: clientKey,
535
- },
536
- data: null,
537
- };
538
- }
539
- else {
540
- return {
541
- error: null,
542
- data: {
543
- app_id: response?.data?.mini_game_id,
544
- name: response?.data?.mini_game_name,
545
- description: response?.data?.description,
546
- client_key: response?.data?.client_key,
547
- request_allowed_domains: response?.data?.trusted_domains,
548
- display_icon_url: response?.data?.icon_url,
549
- privacy_policy_link: response?.data?.privacy_policy,
550
- terms_of_service_link: response?.data?.terms_of_service,
551
- },
552
- };
553
- }
546
+ };
554
547
  }
555
548
  }
556
549
 
557
- async function uploadGameToPlatform({ data, name, clientKey, note = '--', appId, sandboxId, }) {
550
+ async function uploadGameToPlatform({ data, name, clientKey, note = '--', appId, }) {
551
+ if (!appId) {
552
+ return {
553
+ data: null,
554
+ error: {
555
+ message:
556
+ // 可能是登录过期也可能是没有权限导致无法通过 Client key 查询到 appid,请检查网络和登录状态,确保 clientKey 正确。
557
+ 'Upload failed: please check your network and login status, ensure clientKey is correct.',
558
+ },
559
+ };
560
+ }
558
561
  /**
559
562
  * 修复文件名中的非法字符
560
563
  */
561
564
  const fixFilename = (s) => Buffer.from(s, 'latin1').toString('utf8');
562
565
  const safeName = fixFilename(name);
563
- const sanitized = safeName.replace(/[<>:"/\\|?*\u0000-\u001F]/g, '_').slice(0, 255);
564
- if (clientKey?.startsWith('sb')) {
565
- return uploadGameToPlatformV3({
566
- data,
567
- name: sanitized,
568
- clientKey,
569
- note,
570
- appId,
571
- sandboxId,
572
- });
573
- }
574
- else {
575
- return uploadGameToPlatformV4({
576
- data,
577
- name: sanitized,
578
- clientKey,
579
- note,
580
- appId,
581
- });
582
- }
583
- }
584
- async function uploadGameToPlatformV4({ data, name, clientKey, note = '--', appId, }) {
566
+ const sanitized = safeName
567
+ .replace(/[<>:"/\\|?*\u0000-\u001F]/g, '_')
568
+ .slice(0, 255);
585
569
  const formData = new FormData();
586
- formData.append('file', new Blob([data], { type: 'application/zip' }), name);
570
+ formData.append('file', new Blob([data], { type: 'application/zip' }), sanitized);
587
571
  formData.append('client_key', clientKey);
588
572
  formData.append('note', note);
589
573
  const response = await request({
@@ -591,61 +575,31 @@ async function uploadGameToPlatformV4({ data, name, clientKey, note = '--', appI
591
575
  url: 'https://developers.tiktok.com/tiktok/v4/devportal/mini_game/devtool/upload',
592
576
  data: formData,
593
577
  headers: {
594
- // 'x-use-ppe': '1',
595
- // 'x-tt-env': 'ppe_mg_revamp',
596
- // @ts-ignore
597
- 'Content-Type': `multipart/form-data; boundary=${formData._boundary}`,
598
- },
599
- });
600
- const detail = `https://developers.tiktok.com/portal/game/${appId}/code-version`;
601
- return {
602
- data: {
603
- ...response?.data,
604
- detail,
605
- client_key: clientKey,
606
- },
607
- error: response?.error,
608
- };
609
- }
610
- async function uploadGameToPlatformV3({ data, name, clientKey, note = '--', sandboxId, appId, }) {
611
- const formData = new FormData();
612
- formData.append('file', new Blob([data], { type: 'application/zip' }), name);
613
- formData.append('client_key', clientKey);
614
- formData.append('note', note);
615
- const response = await request({
616
- method: 'POST',
617
- url: 'https://developers.tiktok.com/tiktok/v3/devportal/minigame/devtool/upload',
618
- data: formData,
619
- headers: {
620
- // 'x-use-ppe': '1',
621
- // 'x-tt-env': 'ppe_dev_tool',
622
578
  // @ts-ignore
623
579
  'Content-Type': `multipart/form-data; boundary=${formData._boundary}`,
624
580
  },
625
581
  });
626
- if (response?.data?.asset_id) {
627
- const detail = `https://developers.tiktok.com/mini-game/${appId}/sandbox/${sandboxId}/code-assets`;
628
- return {
629
- data: {
630
- ...response?.data,
631
- detail,
632
- client_key: clientKey,
633
- },
634
- error: null,
635
- };
636
- }
637
- else {
582
+ if (!response?.data?.asset_id) {
638
583
  const fileValidationError = response?.data?.file_validation_error?.join(', ');
639
- // 报错
640
584
  return {
641
585
  data: null,
642
586
  error: {
643
- StatusMessage: fileValidationError ||
587
+ message: fileValidationError ||
644
588
  response?.error?.StatusMessage ||
645
589
  'Upload failed, please try again',
646
590
  },
647
591
  };
648
592
  }
593
+ else {
594
+ return {
595
+ data: {
596
+ ...response?.data,
597
+ detail: `https://developers.tiktok.com/portal/game/${appId}/code-version`,
598
+ client_key: clientKey,
599
+ },
600
+ error: response?.error,
601
+ };
602
+ }
649
603
  }
650
604
 
651
605
  function getCurrentUser() {
@@ -664,6 +618,34 @@ function ensureDirSync(dirPath) {
664
618
  }
665
619
  }
666
620
 
621
+ var index = /*#__PURE__*/Object.freeze({
622
+ __proto__: null,
623
+ checkUpdate: checkUpdate,
624
+ download: download,
625
+ ensureDirSync: ensureDirSync,
626
+ fetchGameInfo: fetchGameInfo,
627
+ getCurrentUser: getCurrentUser,
628
+ getLocalIP: getLocalIP,
629
+ getTTMGRC: getTTMGRC,
630
+ login: login,
631
+ openUrl: openUrl,
632
+ request: request,
633
+ setTTMGRC: setTTMGRC,
634
+ setup: setup,
635
+ uploadGameToPlatform: uploadGameToPlatform
636
+ });
637
+
638
+ const bareToRelativePlugin = {
639
+ name: 'bare-to-relative',
640
+ setup(build) {
641
+ // 匹配不以 . 或 / 开头的 .js 文件路径
642
+ build.onResolve({ filter: /^[^./].*\.js$/ }, args => {
643
+ return {
644
+ path: path.resolve(args.resolveDir, args.path),
645
+ };
646
+ });
647
+ },
648
+ };
667
649
  function buildOpenContext(sourcePath) {
668
650
  const result = esbuild.buildSync({
669
651
  entryPoints: [sourcePath],
@@ -680,6 +662,7 @@ function buildOpenContext(sourcePath) {
680
662
  '.svg': 'dataurl',
681
663
  '.webp': 'dataurl',
682
664
  },
665
+ plugins: [bareToRelativePlugin],
683
666
  });
684
667
  const jsCode = result.outputFiles[0].text;
685
668
  return jsCode;
@@ -1028,7 +1011,7 @@ const NATIVE_GAME_ENTRY_FILES = [
1028
1011
  'game.json',
1029
1012
  'project.config.json',
1030
1013
  ];
1031
- const SUBAPCKAGE_FILED_NAMES = ['subpackages', 'subPackages'];
1014
+ const SUBPACKAGE_FIELD_NAMES = ['subpackages', 'subPackages'];
1032
1015
  const SUBPACKAGE_CONFIG_FILE_NAME = 'game.json';
1033
1016
 
1034
1017
  // store.ts
@@ -1073,7 +1056,6 @@ const store = Store.getInstance({
1073
1056
  clientWsHost: '',
1074
1057
  clientKey: '',
1075
1058
  appId: '',
1076
- sandboxId: '',
1077
1059
  nodeServerPort: DEV_PORT,
1078
1060
  nodeWsPort: DEV_WS_PORT,
1079
1061
  packages: {},
@@ -1314,6 +1296,10 @@ async function compile(context) {
1314
1296
  isSuccess: false,
1315
1297
  });
1316
1298
  console.log(chalk.red.bold(errorMsg));
1299
+ // 非 watch 模式下,校验失败直接退出进程
1300
+ if (context?.mode !== 'watch') {
1301
+ process.exit(1);
1302
+ }
1317
1303
  }
1318
1304
  else {
1319
1305
  store.setState({
@@ -1777,21 +1763,6 @@ async function init() {
1777
1763
  }
1778
1764
  }
1779
1765
 
1780
- /**
1781
- * 检查当前目录是否为 Mini Game 项目的入口目录
1782
- */
1783
- function checkEntry() {
1784
- const entryFiles = NATIVE_GAME_ENTRY_FILES.map(file => path.join(process.cwd(), file));
1785
- const foundEntryFile = entryFiles.find(file => fs.existsSync(file));
1786
- if (!foundEntryFile) {
1787
- /**
1788
- * 如果当前目录下没有任何一个入口文件,提示用户检查当前目录是否为 Mini Game 项目的入口目录,需要提醒开发者进入到游戏项目根目录进行调试
1789
- */
1790
- console.error(chalk.red.bold(`Current directory is not a Mini Game project entry directory, please enter the game project root directory for debugging`));
1791
- process.exit(1);
1792
- }
1793
- }
1794
-
1795
1766
  function getDevToolVersion() {
1796
1767
  try {
1797
1768
  return JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8')).version;
@@ -1864,6 +1835,92 @@ const zipCwdToBuffer = (customIgnores = []) => {
1864
1835
  });
1865
1836
  };
1866
1837
 
1838
+ /**
1839
+ * 检查当前目录是否为 Mini Game 项目的入口目录
1840
+ */
1841
+ function isWorkspace() {
1842
+ const entryFiles = NATIVE_GAME_ENTRY_FILES.map(file => path.join(process.cwd(), file));
1843
+ const foundEntryFile = entryFiles.find(file => fs.existsSync(file));
1844
+ if (!foundEntryFile) {
1845
+ return false;
1846
+ }
1847
+ else {
1848
+ return true;
1849
+ }
1850
+ }
1851
+
1852
+ async function check() {
1853
+ const boxenFn = boxen.default ??
1854
+ boxen;
1855
+ const printBox = (type, message) => {
1856
+ const borderColor = 'red' ;
1857
+ const log = console.error ;
1858
+ log(boxenFn(chalk.white(message), {
1859
+ title: type,
1860
+ titleAlignment: 'left',
1861
+ borderStyle: 'round',
1862
+ borderColor,
1863
+ padding: 1,
1864
+ }));
1865
+ };
1866
+ // if (!isLogin()) {
1867
+ // printBox(
1868
+ // 'Warning',
1869
+ // `You are not logged in, please login first, run ttmg login to login with your TikTok Dev Portal account, then try again`,
1870
+ // );
1871
+ // process.exit(1);
1872
+ // }
1873
+ if (!isWorkspace()) {
1874
+ printBox('Error', `Current directory is not a Mini Game project entry directory, please enter the game project root directory for debugging`);
1875
+ process.exit(1);
1876
+ }
1877
+ // const checkResult = await checkPkgs({
1878
+ // entry: process.cwd(),
1879
+ // output: process.cwd(),
1880
+ // dev: {
1881
+ // enable: true,
1882
+ // enableSourcemap: false,
1883
+ // enableLog: false,
1884
+ // },
1885
+ // build: {
1886
+ // enableOdr: false,
1887
+ // enableAPICheck: true,
1888
+ // ...PKG_SIZE_LIMIT,
1889
+ // },
1890
+ // });
1891
+ // const errorResults = checkResult.filter(
1892
+ // item => !item.passed && item.level === 'error',
1893
+ // );
1894
+ // const warningResults = checkResult.filter(
1895
+ // item => !item.passed && item.level === 'warning',
1896
+ // );
1897
+ // if (warningResults.length > 0) {
1898
+ // const warningText = warningResults.map(item => item.msg).join('\n');
1899
+ // console.warn(
1900
+ // boxenFn(chalk.white(warningText), {
1901
+ // title: 'Warning',
1902
+ // titleAlignment: 'left',
1903
+ // borderStyle: 'round',
1904
+ // borderColor: 'yellow',
1905
+ // padding: 1,
1906
+ // }),
1907
+ // );
1908
+ // }
1909
+ // if (errorResults.length > 0) {
1910
+ // const errorText = errorResults.map(item => item.msg).join('\n');
1911
+ // console.error(
1912
+ // boxenFn(chalk.white(errorText), {
1913
+ // title: 'Error',
1914
+ // titleAlignment: 'left',
1915
+ // borderStyle: 'round',
1916
+ // borderColor: 'red',
1917
+ // padding: 1,
1918
+ // }),
1919
+ // );
1920
+ // process.exit(1);
1921
+ // }
1922
+ }
1923
+
1867
1924
  const BASE_URL = 'https://developers.tiktok.com';
1868
1925
  const DEV_HEADERS = {
1869
1926
  // 'x-use-ppe': '1',
@@ -2388,8 +2445,8 @@ function updateSubpackageConfigSync() {
2388
2445
  const gameJsonPath = path__namespace.join(process.cwd(), SUBPACKAGE_CONFIG_FILE_NAME);
2389
2446
  const raw = fs__namespace.readFileSync(gameJsonPath, 'utf-8');
2390
2447
  const gameJson = JSON.parse(raw);
2391
- const fieldName = SUBAPCKAGE_FILED_NAMES.find(k => k in gameJson) ??
2392
- SUBAPCKAGE_FILED_NAMES[0];
2448
+ const fieldName = SUBPACKAGE_FIELD_NAMES.find(k => k in gameJson) ??
2449
+ SUBPACKAGE_FIELD_NAMES[0];
2393
2450
  if (!gameJson[fieldName])
2394
2451
  gameJson[fieldName] = [];
2395
2452
  const subpackages = gameJson[fieldName];
@@ -2801,7 +2858,6 @@ async function start() {
2801
2858
  const { error, data: gameInfo } = await fetchGameInfo(clientKey);
2802
2859
  store.setState({
2803
2860
  appId: gameInfo?.app_id,
2804
- sandboxId: gameInfo?.sandbox_info?.sandbox_id,
2805
2861
  });
2806
2862
  if (error) {
2807
2863
  res.send({ error, data: null });
@@ -2813,68 +2869,23 @@ async function start() {
2813
2869
  });
2814
2870
  app.get('/game/check', async (req, res) => {
2815
2871
  const checkResult = await ttmgPack.checkPkgs({
2816
- entryDir: process.cwd(),
2817
- config: {
2818
- entry: process.cwd(),
2819
- output: outputDir,
2820
- dev: {
2821
- enable: true,
2822
- port: store.getState().nodeServerPort,
2823
- host: 'localhost',
2824
- enableSourcemap: false,
2825
- enableLog: false,
2826
- },
2827
- build: {
2828
- enableOdr: false,
2829
- enableAPICheck: true,
2830
- ...PKG_SIZE_LIMIT,
2831
- },
2872
+ entry: process.cwd(),
2873
+ output: outputDir,
2874
+ dev: {
2875
+ enable: true,
2876
+ port: store.getState().nodeServerPort,
2877
+ host: 'localhost',
2878
+ enableSourcemap: false,
2879
+ enableLog: false,
2880
+ },
2881
+ build: {
2882
+ enableOdr: false,
2883
+ enableAPICheck: true,
2884
+ ...PKG_SIZE_LIMIT,
2832
2885
  },
2833
2886
  });
2834
2887
  res.send({ code: successCode, data: checkResult });
2835
2888
  });
2836
- /**
2837
- * @description 上传游戏代码到服务器
2838
- */
2839
- // app.post('/game/upload', async (req, res) => {
2840
- // /**
2841
- // * 我要新版本不做开发者选择,直接当前 cwd 作为 entryDir,压缩成 game.zip 进行上传
2842
- // */
2843
- // const fileKeys = Object.keys(req.files);
2844
- // const uploadedFile = req.files[fileKeys[0]];
2845
- // if (!uploadedFile) {
2846
- // res.status(400).send({ code: errorCode, data: 'No file uploaded' }); // 使用正确的 HTTP 状态码
2847
- // return;
2848
- // }
2849
- // try {
2850
- // // 通过 header 获取 desc
2851
- // const desc = req.headers['ttmg-game-desc'];
2852
- // // 需要做 decodeURIComponent 处理
2853
- // const decodedDesc = decodeURIComponent(desc || '--');
2854
- // // 直接传递需要的信息
2855
- // const { data, error } = await uploadGameToPlatform({
2856
- // data: uploadedFile.data, // 这是 Buffer
2857
- // name: uploadedFile.name, // 这是文件名
2858
- // clientKey: getClientKey().clientKey,
2859
- // note: decodedDesc,
2860
- // appId: store.getState().appId,
2861
- // sandboxId: store.getState().sandboxId,
2862
- // });
2863
- // if (error) {
2864
- // res.send({ code: errorCode, error });
2865
- // } else {
2866
- // res.send({ code: successCode, data });
2867
- // }
2868
- // } catch (error) {
2869
- // // 错误处理可以更具体
2870
- // let errorMessage = 'An unknown error occurred.';
2871
- // if (error instanceof Error) {
2872
- // errorMessage = error.message;
2873
- // }
2874
- // // 打印详细错误到服务器日志,方便排查
2875
- // res.status(500).send({ code: errorCode, data: errorMessage }); // 使用正确的 HTTP 状态码
2876
- // }
2877
- // });
2878
2889
  app.post('/game/upload', async (req, res) => {
2879
2890
  try {
2880
2891
  console.log(`正在打包当前目录: ${process.cwd()} ...`);
@@ -2890,7 +2901,6 @@ async function start() {
2890
2901
  clientKey: getClientKey().clientKey,
2891
2902
  note: decodedDesc,
2892
2903
  appId: store.getState().appId,
2893
- sandboxId: store.getState().sandboxId,
2894
2904
  });
2895
2905
  if (error) {
2896
2906
  res.send({ code: errorCode, error });
@@ -3172,29 +3182,7 @@ async function start() {
3172
3182
  }
3173
3183
  });
3174
3184
  /**
3175
- *
3176
- 高鹏
3177
- 抄一下码下面的话
3178
- 我试试这个咋样看[看]闵行的是好吃的
3179
- 可以
3180
- 下周大哥来 让他别整一群的 就咱们几个吃个串挺好
3181
- 哪个unity筹备群拉你了哈
3182
-
3183
- 新知识 30% 等于部分用户
3184
- 0.3.1-unity.6
3185
- {
3186
- "code": 0,
3187
- "data": {
3188
- "isSuccess": true
3189
- },
3190
- "msg": "download success",
3191
- "ctx":
3192
- }
3193
- 发现个小问题
3194
- wasmcode1-ios 下面下载缺了一个func_bytes_range.json 以及subjs.data.br 多了一个 br 后缀
3195
- ​​​
3196
- Shift + Enter 换行
3197
-
3185
+ * @description 下载分包产物后,查询并返回下载结果
3198
3186
  */
3199
3187
  app.post('/game/wasm-split-download-result', async (req, res) => {
3200
3188
  const { clientKey, codeMd5, codePath } = req.body;
@@ -3341,7 +3329,7 @@ async function start() {
3341
3329
  }
3342
3330
 
3343
3331
  async function dev() {
3344
- checkEntry();
3332
+ await check();
3345
3333
  await init();
3346
3334
  await start();
3347
3335
  await compile();
@@ -3349,7 +3337,7 @@ async function dev() {
3349
3337
  watch();
3350
3338
  }
3351
3339
 
3352
- var version = "0.3.1";
3340
+ var version = "0.3.2-beta.3";
3353
3341
  var pkg = {
3354
3342
  version: version};
3355
3343
 
@@ -3363,8 +3351,16 @@ program
3363
3351
  program
3364
3352
  .command('login')
3365
3353
  .description('User Dev Portal Account to Login')
3366
- .action(async () => {
3367
- await login();
3354
+ .option('--verbose', 'Print verbose logs for debugging')
3355
+ .action(async (cmd) => {
3356
+ await login({ verbose: cmd.verbose });
3357
+ });
3358
+ program
3359
+ .command('setup')
3360
+ .description('Initialize ttmg environment')
3361
+ .option('--lang <lang>', 'Language: en-US | zh-CN')
3362
+ .action(async (cmd) => {
3363
+ await setup({ lang: cmd.lang });
3368
3364
  });
3369
3365
  program
3370
3366
  .option('--h5', 'H5 Mini Game')
@@ -3387,6 +3383,12 @@ program
3387
3383
  .command('dev')
3388
3384
  .description('Open browser dev environment')
3389
3385
  .action(async () => {
3386
+ const { getCurrentUser } = await Promise.resolve().then(function () { return index; });
3387
+ const user = getCurrentUser();
3388
+ if (!user) {
3389
+ console.log('\n❌ 请先登录 TikTok 账号,执行命令: ttmg login\n');
3390
+ process.exit(1);
3391
+ }
3390
3392
  const options = program.opts();
3391
3393
  if (options.h5) {
3392
3394
  await dev$1();