@ttmg/cli 0.3.2-beta.1 → 0.3.2-beta.4

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
@@ -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');
@@ -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)) {
@@ -273,6 +274,11 @@ const setTTMGRC = (config) => {
273
274
  fs.writeFileSync(CONFIG_PATH, JSON.stringify({ ...originConfig, ...config }));
274
275
  };
275
276
 
277
+ function printMessage(type, message) {
278
+ const prefix = type === 'Error' ? chalk.red('Error: ') : chalk.yellow('Warning: ');
279
+ const log = type === 'Error' ? console.error : console.warn;
280
+ log(prefix + message);
281
+ }
276
282
  let spinner$1;
277
283
  const LOGIN_TT4D = 'https://developers.tiktok.com/passport/web/email/login';
278
284
  const params = {
@@ -281,7 +287,15 @@ const params = {
281
287
  sdk_version: '2.1.6-tiktok',
282
288
  };
283
289
  const prompt = inquirer.createPromptModule();
284
- async function login() {
290
+ async function login(options) {
291
+ const verbose = options?.verbose === true;
292
+ const log = (msg, data) => {
293
+ if (!verbose)
294
+ return;
295
+ console.log(chalk.gray('[ttmg login]'), msg, data !== undefined ? data : '');
296
+ };
297
+ if (verbose)
298
+ log('Verbose logging enabled');
285
299
  console.log(chalk.yellowBright('⚠️ Note: Please login with your TikTok Developer Platform account.'));
286
300
  // 增加对邮箱密码的校验
287
301
  const { email, password } = await prompt([
@@ -318,6 +332,8 @@ async function login() {
318
332
  },
319
333
  ]);
320
334
  const url = LOGIN_TT4D + '?' + new URLSearchParams(params);
335
+ log('Request URL', url);
336
+ log('Request params', { ...params, email: email.replace(/(.{2}).*(@.*)/, '$1***$2') });
321
337
  const headers = {
322
338
  'Content-Type': 'application/x-www-form-urlencoded',
323
339
  Accept: '*/*',
@@ -339,20 +355,51 @@ async function login() {
339
355
  fixed_mix_mode: '1',
340
356
  });
341
357
  try {
358
+ log('Sending POST request...');
342
359
  const response = await axios.post(url, data, {
343
360
  headers,
344
361
  maxRedirects: 20,
345
362
  timeout: 30000,
346
363
  });
364
+ log('Response status', response.status);
365
+ log('Response data', response?.data);
347
366
  if (!response?.data?.data?.user_id) {
348
367
  const errCode = response.data?.data?.error_code;
349
368
  const errMsg = response.data?.data?.description;
369
+ const statusText = response?.statusText ?? '';
370
+ spinner$1.fail(chalk.red('login failed'));
371
+ if (verbose) {
372
+ console.log(chalk.gray('Response status:'), response?.status);
373
+ console.log(chalk.gray('Response statusText:'), statusText || '(empty)');
374
+ console.log(chalk.gray('Response data:'), response?.data ?? '(empty)');
375
+ console.log('');
376
+ }
350
377
  if (errCode || errMsg) {
351
- spinner$1.fail(chalk.red(`login failed: ${errMsg}${errCode ? `, error_code: ${errCode}` : ''}`));
378
+ log('Login failed (api)', { errCode, errMsg, fullData: response?.data });
379
+ printMessage('Error', `Login failed: ${errMsg}${errCode ? `, error_code: ${errCode}` : ''}`);
352
380
  }
353
381
  else {
354
- spinner$1.fail(chalk.red(`login failed`));
355
- console.log(response.data);
382
+ log('Login failed (no user_id)', { responseBody: response?.data });
383
+ if (verbose)
384
+ log('Full response (for debugging)', response);
385
+ const looksLikeProxyResponse = statusText === 'Connection established' ||
386
+ (response?.status === 200 &&
387
+ (response?.data == null ||
388
+ typeof response.data !== 'object' ||
389
+ !('data' in response.data)));
390
+ if (looksLikeProxyResponse) {
391
+ printMessage('Warning', [
392
+ 'The response does not look like the login API (e.g. proxy returned "Connection established" instead of forwarding the real response).',
393
+ '',
394
+ 'The API is not reachable from mainland without proxy. Please ensure your proxy correctly forwards HTTPS to developers.tiktok.com: try another proxy node, or check that the proxy is in global/tunnel mode and not intercepting HTTPS.',
395
+ '',
396
+ 'Proxy troubleshooting doc:',
397
+ 'https://bytedance.larkoffice.com/wiki/ZblJwT0ZNil9jJkS8EgcFlcQnFc',
398
+ ].join('\n'));
399
+ }
400
+ else {
401
+ printMessage('Error', 'Login failed. No user_id in response.');
402
+ }
356
403
  }
357
404
  spinner$1.stop();
358
405
  process.exit(1);
@@ -363,23 +410,44 @@ async function login() {
363
410
  user_id: response?.data?.data?.user_id,
364
411
  cookie: response?.headers['set-cookie'].join('; '),
365
412
  };
413
+ log('Login success', { user_id: data.user_id, email: data.email });
366
414
  setTTMGRC(data);
367
415
  spinner$1.succeed(chalk.bold.green('login successfully!'));
368
416
  process.exit(0);
369
417
  }
370
418
  }
371
419
  catch (error) {
372
- // 1. Error Title: Red and bold
420
+ log('Request error', error instanceof Error ? { message: error.message, name: error.name, stack: error.stack } : error);
373
421
  spinner$1.fail(chalk.red.bold('Failed to connect to login service'));
374
- // 2. Description: Normal text with bold keywords
375
- console.log(" Detected that the current terminal's " +
376
- chalk.bold('network proxy settings') +
377
- ' are preventing external network access.', chalk.yellow('Please check your local terminal proxy configuration.'));
378
- console.log(chalk.yellow('You can follow this doc to modify your terminal network settings:'), chalk.underline.yellow('https://bytedance.larkoffice.com/wiki/ZblJwT0ZNil9jJkS8EgcFlcQnFc'));
422
+ printMessage('Error', [
423
+ "Detected that the current terminal's network proxy settings are preventing external network access.",
424
+ '',
425
+ 'Please check your local terminal proxy configuration. You can follow this doc to modify your terminal network settings:',
426
+ 'https://bytedance.larkoffice.com/wiki/ZblJwT0ZNil9jJkS8EgcFlcQnFc',
427
+ ].join('\n'));
379
428
  process.exit(1);
380
429
  }
381
430
  }
382
431
 
432
+ async function setup(options) {
433
+ const inputLang = options?.lang;
434
+ const normalizedLang = inputLang === 'en-US' || inputLang === 'zh-CN' ? inputLang : null;
435
+ const lang = normalizedLang ??
436
+ (await inquirer.createPromptModule()([
437
+ {
438
+ type: 'list',
439
+ name: 'lang',
440
+ message: 'Select language your prefer',
441
+ choices: [
442
+ { name: 'English (en-US)', value: 'en-US' },
443
+ { name: '简体中文 (zh-CN)', value: 'zh-CN' },
444
+ ],
445
+ default: 'en-US',
446
+ },
447
+ ])).lang;
448
+ setTTMGRC({ lang });
449
+ }
450
+
383
451
  // ppe_dev_tool
384
452
  async function request({ url, method, data, headers, params, }) {
385
453
  const config = getTTMGRC();
@@ -392,6 +460,8 @@ async function request({ url, method, data, headers, params, }) {
392
460
  params,
393
461
  headers: {
394
462
  Cookie: cookie,
463
+ 'x-use-ppe': '1',
464
+ 'x-ppe-env': 'ppe_upgrade_script',
395
465
  ...(headers || {}),
396
466
  },
397
467
  });
@@ -475,115 +545,62 @@ function isAxiosError(e) {
475
545
  }
476
546
 
477
547
  async function fetchGameInfo(clientKey) {
478
- if (clientKey?.startsWith('sb')) {
479
- return await fetchGameInfoV3(clientKey);
548
+ // 访问 V4 接口
549
+ const response = await request({
550
+ url: `https://developers.tiktok.com/tiktok/v4/devportal/mini_game/devtool/info`,
551
+ method: 'GET',
552
+ params: {
553
+ client_key: clientKey,
554
+ },
555
+ });
556
+ if (!response?.data) {
557
+ return {
558
+ error: {
559
+ errorCode: response?.error?.StatusCode,
560
+ errorMsg: response?.error?.StatusMessage,
561
+ clientKey: clientKey,
562
+ },
563
+ data: null,
564
+ };
480
565
  }
481
566
  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,
567
+ return {
568
+ error: null,
569
+ data: {
570
+ app_id: response?.data?.mini_game_id,
571
+ name: response?.data?.mini_game_name,
572
+ description: response?.data?.description,
573
+ client_key: response?.data?.client_key,
574
+ request_allowed_domains: response?.data?.trusted_domains,
575
+ display_icon_url: response?.data?.icon_url,
576
+ privacy_policy_link: response?.data?.privacy_policy,
577
+ terms_of_service_link: response?.data?.terms_of_service,
492
578
  },
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
- }
579
+ };
554
580
  }
555
581
  }
556
582
 
557
- async function uploadGameToPlatform({ data, name, clientKey, note = '--', appId, sandboxId, }) {
583
+ async function uploadGameToPlatform({ data, name, clientKey, note = '--', appId, }) {
584
+ if (!appId) {
585
+ return {
586
+ data: null,
587
+ error: {
588
+ message:
589
+ // 可能是登录过期也可能是没有权限导致无法通过 Client key 查询到 appid,请检查网络和登录状态,确保 clientKey 正确。
590
+ 'Upload failed: please check your network and login status, ensure clientKey is correct.',
591
+ },
592
+ };
593
+ }
558
594
  /**
559
595
  * 修复文件名中的非法字符
560
596
  */
561
597
  const fixFilename = (s) => Buffer.from(s, 'latin1').toString('utf8');
562
598
  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, }) {
599
+ const sanitized = safeName
600
+ .replace(/[<>:"/\\|?*\u0000-\u001F]/g, '_')
601
+ .slice(0, 255);
585
602
  const formData = new FormData();
586
- formData.append('file', new Blob([data], { type: 'application/zip' }), name);
603
+ formData.append('file', new Blob([data], { type: 'application/zip' }), sanitized);
587
604
  formData.append('client_key', clientKey);
588
605
  formData.append('note', note);
589
606
  const response = await request({
@@ -591,61 +608,31 @@ async function uploadGameToPlatformV4({ data, name, clientKey, note = '--', appI
591
608
  url: 'https://developers.tiktok.com/tiktok/v4/devportal/mini_game/devtool/upload',
592
609
  data: formData,
593
610
  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
611
  // @ts-ignore
623
612
  'Content-Type': `multipart/form-data; boundary=${formData._boundary}`,
624
613
  },
625
614
  });
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 {
615
+ if (!response?.data?.asset_id) {
638
616
  const fileValidationError = response?.data?.file_validation_error?.join(', ');
639
- // 报错
640
617
  return {
641
618
  data: null,
642
619
  error: {
643
- StatusMessage: fileValidationError ||
620
+ message: fileValidationError ||
644
621
  response?.error?.StatusMessage ||
645
622
  'Upload failed, please try again',
646
623
  },
647
624
  };
648
625
  }
626
+ else {
627
+ return {
628
+ data: {
629
+ ...response?.data,
630
+ detail: `https://developers.tiktok.com/portal/game/${appId}/code-version`,
631
+ client_key: clientKey,
632
+ },
633
+ error: response?.error,
634
+ };
635
+ }
649
636
  }
650
637
 
651
638
  function getCurrentUser() {
@@ -1040,7 +1027,7 @@ const NATIVE_GAME_ENTRY_FILES = [
1040
1027
  'game.json',
1041
1028
  'project.config.json',
1042
1029
  ];
1043
- const SUBAPCKAGE_FILED_NAMES = ['subpackages', 'subPackages'];
1030
+ const SUBPACKAGE_FIELD_NAMES = ['subpackages', 'subPackages'];
1044
1031
  const SUBPACKAGE_CONFIG_FILE_NAME = 'game.json';
1045
1032
 
1046
1033
  // store.ts
@@ -1085,7 +1072,6 @@ const store = Store.getInstance({
1085
1072
  clientWsHost: '',
1086
1073
  clientKey: '',
1087
1074
  appId: '',
1088
- sandboxId: '',
1089
1075
  nodeServerPort: DEV_PORT,
1090
1076
  nodeWsPort: DEV_WS_PORT,
1091
1077
  packages: {},
@@ -1326,6 +1312,10 @@ async function compile(context) {
1326
1312
  isSuccess: false,
1327
1313
  });
1328
1314
  console.log(chalk.red.bold(errorMsg));
1315
+ // 非 watch 模式下,校验失败直接退出进程
1316
+ if (context?.mode !== 'watch') {
1317
+ process.exit(1);
1318
+ }
1329
1319
  }
1330
1320
  else {
1331
1321
  store.setState({
@@ -1789,21 +1779,6 @@ async function init() {
1789
1779
  }
1790
1780
  }
1791
1781
 
1792
- /**
1793
- * 检查当前目录是否为 Mini Game 项目的入口目录
1794
- */
1795
- function checkEntry() {
1796
- const entryFiles = NATIVE_GAME_ENTRY_FILES.map(file => path.join(process.cwd(), file));
1797
- const foundEntryFile = entryFiles.find(file => fs.existsSync(file));
1798
- if (!foundEntryFile) {
1799
- /**
1800
- * 如果当前目录下没有任何一个入口文件,提示用户检查当前目录是否为 Mini Game 项目的入口目录,需要提醒开发者进入到游戏项目根目录进行调试
1801
- */
1802
- console.error(chalk.red.bold(`Current directory is not a Mini Game project entry directory, please enter the game project root directory for debugging`));
1803
- process.exit(1);
1804
- }
1805
- }
1806
-
1807
1782
  function getDevToolVersion() {
1808
1783
  try {
1809
1784
  return JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8')).version;
@@ -1876,6 +1851,92 @@ const zipCwdToBuffer = (customIgnores = []) => {
1876
1851
  });
1877
1852
  };
1878
1853
 
1854
+ /**
1855
+ * 检查当前目录是否为 Mini Game 项目的入口目录
1856
+ */
1857
+ function isWorkspace() {
1858
+ const entryFiles = NATIVE_GAME_ENTRY_FILES.map(file => path.join(process.cwd(), file));
1859
+ const foundEntryFile = entryFiles.find(file => fs.existsSync(file));
1860
+ if (!foundEntryFile) {
1861
+ return false;
1862
+ }
1863
+ else {
1864
+ return true;
1865
+ }
1866
+ }
1867
+
1868
+ async function check() {
1869
+ const boxenFn = boxen.default ??
1870
+ boxen;
1871
+ const printBox = (type, message) => {
1872
+ const borderColor = 'red' ;
1873
+ const log = console.error ;
1874
+ log(boxenFn(chalk.white(message), {
1875
+ title: type,
1876
+ titleAlignment: 'left',
1877
+ borderStyle: 'round',
1878
+ borderColor,
1879
+ padding: 1,
1880
+ }));
1881
+ };
1882
+ // if (!isLogin()) {
1883
+ // printBox(
1884
+ // 'Warning',
1885
+ // `You are not logged in, please login first, run ttmg login to login with your TikTok Dev Portal account, then try again`,
1886
+ // );
1887
+ // process.exit(1);
1888
+ // }
1889
+ if (!isWorkspace()) {
1890
+ printBox('Error', `Current directory is not a Mini Game project entry directory, please enter the game project root directory for debugging`);
1891
+ process.exit(1);
1892
+ }
1893
+ // const checkResult = await checkPkgs({
1894
+ // entry: process.cwd(),
1895
+ // output: process.cwd(),
1896
+ // dev: {
1897
+ // enable: true,
1898
+ // enableSourcemap: false,
1899
+ // enableLog: false,
1900
+ // },
1901
+ // build: {
1902
+ // enableOdr: false,
1903
+ // enableAPICheck: true,
1904
+ // ...PKG_SIZE_LIMIT,
1905
+ // },
1906
+ // });
1907
+ // const errorResults = checkResult.filter(
1908
+ // item => !item.passed && item.level === 'error',
1909
+ // );
1910
+ // const warningResults = checkResult.filter(
1911
+ // item => !item.passed && item.level === 'warning',
1912
+ // );
1913
+ // if (warningResults.length > 0) {
1914
+ // const warningText = warningResults.map(item => item.msg).join('\n');
1915
+ // console.warn(
1916
+ // boxenFn(chalk.white(warningText), {
1917
+ // title: 'Warning',
1918
+ // titleAlignment: 'left',
1919
+ // borderStyle: 'round',
1920
+ // borderColor: 'yellow',
1921
+ // padding: 1,
1922
+ // }),
1923
+ // );
1924
+ // }
1925
+ // if (errorResults.length > 0) {
1926
+ // const errorText = errorResults.map(item => item.msg).join('\n');
1927
+ // console.error(
1928
+ // boxenFn(chalk.white(errorText), {
1929
+ // title: 'Error',
1930
+ // titleAlignment: 'left',
1931
+ // borderStyle: 'round',
1932
+ // borderColor: 'red',
1933
+ // padding: 1,
1934
+ // }),
1935
+ // );
1936
+ // process.exit(1);
1937
+ // }
1938
+ }
1939
+
1879
1940
  const BASE_URL = 'https://developers.tiktok.com';
1880
1941
  const DEV_HEADERS = {
1881
1942
  // 'x-use-ppe': '1',
@@ -2400,8 +2461,8 @@ function updateSubpackageConfigSync() {
2400
2461
  const gameJsonPath = path__namespace.join(process.cwd(), SUBPACKAGE_CONFIG_FILE_NAME);
2401
2462
  const raw = fs__namespace.readFileSync(gameJsonPath, 'utf-8');
2402
2463
  const gameJson = JSON.parse(raw);
2403
- const fieldName = SUBAPCKAGE_FILED_NAMES.find(k => k in gameJson) ??
2404
- SUBAPCKAGE_FILED_NAMES[0];
2464
+ const fieldName = SUBPACKAGE_FIELD_NAMES.find(k => k in gameJson) ??
2465
+ SUBPACKAGE_FIELD_NAMES[0];
2405
2466
  if (!gameJson[fieldName])
2406
2467
  gameJson[fieldName] = [];
2407
2468
  const subpackages = gameJson[fieldName];
@@ -2791,7 +2852,25 @@ async function start() {
2791
2852
  app.use(express.urlencoded({ extended: true }));
2792
2853
  app.use('/game/files', express.static(outputDir));
2793
2854
  app.get('/game/config', async (req, res) => {
2794
- const basic = await ttmgPack.getPkgs({ entryDir: process.cwd() });
2855
+ const [basic, checkResult] = await Promise.all([
2856
+ ttmgPack.getPkgs({ entryDir: process.cwd() }),
2857
+ ttmgPack.checkPkgs({
2858
+ entry: process.cwd(),
2859
+ output: outputDir,
2860
+ dev: {
2861
+ enable: true,
2862
+ port: store.getState().nodeServerPort,
2863
+ host: 'localhost',
2864
+ enableSourcemap: false,
2865
+ enableLog: false,
2866
+ },
2867
+ build: {
2868
+ enableOdr: false,
2869
+ enableAPICheck: true,
2870
+ ...PKG_SIZE_LIMIT,
2871
+ },
2872
+ }),
2873
+ ]);
2795
2874
  const user = getCurrentUser();
2796
2875
  const { clientKey } = getClientKey();
2797
2876
  res.send({
@@ -2804,6 +2883,7 @@ async function start() {
2804
2883
  schema: `https://www.tiktok.com/ttmg/dev/${clientKey}?host=${getLocalIP()}&port=${store.getState().nodeWsPort}&host_list=${encodeURIComponent(JSON.stringify(getLocalIPs()))}`,
2805
2884
  ...basic,
2806
2885
  devToolVersion,
2886
+ checkResult,
2807
2887
  },
2808
2888
  });
2809
2889
  });
@@ -2813,7 +2893,6 @@ async function start() {
2813
2893
  const { error, data: gameInfo } = await fetchGameInfo(clientKey);
2814
2894
  store.setState({
2815
2895
  appId: gameInfo?.app_id,
2816
- sandboxId: gameInfo?.sandbox_info?.sandbox_id,
2817
2896
  });
2818
2897
  if (error) {
2819
2898
  res.send({ error, data: null });
@@ -2825,68 +2904,23 @@ async function start() {
2825
2904
  });
2826
2905
  app.get('/game/check', async (req, res) => {
2827
2906
  const checkResult = await ttmgPack.checkPkgs({
2828
- entryDir: process.cwd(),
2829
- config: {
2830
- entry: process.cwd(),
2831
- output: outputDir,
2832
- dev: {
2833
- enable: true,
2834
- port: store.getState().nodeServerPort,
2835
- host: 'localhost',
2836
- enableSourcemap: false,
2837
- enableLog: false,
2838
- },
2839
- build: {
2840
- enableOdr: false,
2841
- enableAPICheck: true,
2842
- ...PKG_SIZE_LIMIT,
2843
- },
2907
+ entry: process.cwd(),
2908
+ output: outputDir,
2909
+ dev: {
2910
+ enable: true,
2911
+ port: store.getState().nodeServerPort,
2912
+ host: 'localhost',
2913
+ enableSourcemap: false,
2914
+ enableLog: false,
2915
+ },
2916
+ build: {
2917
+ enableOdr: false,
2918
+ enableAPICheck: true,
2919
+ ...PKG_SIZE_LIMIT,
2844
2920
  },
2845
2921
  });
2846
2922
  res.send({ code: successCode, data: checkResult });
2847
2923
  });
2848
- /**
2849
- * @description 上传游戏代码到服务器
2850
- */
2851
- // app.post('/game/upload', async (req, res) => {
2852
- // /**
2853
- // * 我要新版本不做开发者选择,直接当前 cwd 作为 entryDir,压缩成 game.zip 进行上传
2854
- // */
2855
- // const fileKeys = Object.keys(req.files);
2856
- // const uploadedFile = req.files[fileKeys[0]];
2857
- // if (!uploadedFile) {
2858
- // res.status(400).send({ code: errorCode, data: 'No file uploaded' }); // 使用正确的 HTTP 状态码
2859
- // return;
2860
- // }
2861
- // try {
2862
- // // 通过 header 获取 desc
2863
- // const desc = req.headers['ttmg-game-desc'];
2864
- // // 需要做 decodeURIComponent 处理
2865
- // const decodedDesc = decodeURIComponent(desc || '--');
2866
- // // 直接传递需要的信息
2867
- // const { data, error } = await uploadGameToPlatform({
2868
- // data: uploadedFile.data, // 这是 Buffer
2869
- // name: uploadedFile.name, // 这是文件名
2870
- // clientKey: getClientKey().clientKey,
2871
- // note: decodedDesc,
2872
- // appId: store.getState().appId,
2873
- // sandboxId: store.getState().sandboxId,
2874
- // });
2875
- // if (error) {
2876
- // res.send({ code: errorCode, error });
2877
- // } else {
2878
- // res.send({ code: successCode, data });
2879
- // }
2880
- // } catch (error) {
2881
- // // 错误处理可以更具体
2882
- // let errorMessage = 'An unknown error occurred.';
2883
- // if (error instanceof Error) {
2884
- // errorMessage = error.message;
2885
- // }
2886
- // // 打印详细错误到服务器日志,方便排查
2887
- // res.status(500).send({ code: errorCode, data: errorMessage }); // 使用正确的 HTTP 状态码
2888
- // }
2889
- // });
2890
2924
  app.post('/game/upload', async (req, res) => {
2891
2925
  try {
2892
2926
  console.log(`正在打包当前目录: ${process.cwd()} ...`);
@@ -2902,7 +2936,6 @@ async function start() {
2902
2936
  clientKey: getClientKey().clientKey,
2903
2937
  note: decodedDesc,
2904
2938
  appId: store.getState().appId,
2905
- sandboxId: store.getState().sandboxId,
2906
2939
  });
2907
2940
  if (error) {
2908
2941
  res.send({ code: errorCode, error });
@@ -3184,29 +3217,7 @@ async function start() {
3184
3217
  }
3185
3218
  });
3186
3219
  /**
3187
- *
3188
- 高鹏
3189
- 抄一下码下面的话
3190
- 我试试这个咋样看[看]闵行的是好吃的
3191
- 可以
3192
- 下周大哥来 让他别整一群的 就咱们几个吃个串挺好
3193
- 哪个unity筹备群拉你了哈
3194
-
3195
- 新知识 30% 等于部分用户
3196
- 0.3.1-unity.6
3197
- {
3198
- "code": 0,
3199
- "data": {
3200
- "isSuccess": true
3201
- },
3202
- "msg": "download success",
3203
- "ctx":
3204
- }
3205
- 发现个小问题
3206
- wasmcode1-ios 下面下载缺了一个func_bytes_range.json 以及subjs.data.br 多了一个 br 后缀
3207
- ​​​
3208
- Shift + Enter 换行
3209
-
3220
+ * @description 下载分包产物后,查询并返回下载结果
3210
3221
  */
3211
3222
  app.post('/game/wasm-split-download-result', async (req, res) => {
3212
3223
  const { clientKey, codeMd5, codePath } = req.body;
@@ -3353,7 +3364,7 @@ async function start() {
3353
3364
  }
3354
3365
 
3355
3366
  async function dev() {
3356
- checkEntry();
3367
+ await check();
3357
3368
  await init();
3358
3369
  await start();
3359
3370
  await compile();
@@ -3361,7 +3372,7 @@ async function dev() {
3361
3372
  watch();
3362
3373
  }
3363
3374
 
3364
- var version = "0.3.2-beta.1";
3375
+ var version = "0.3.2-beta.4";
3365
3376
  var pkg = {
3366
3377
  version: version};
3367
3378
 
@@ -3375,8 +3386,16 @@ program
3375
3386
  program
3376
3387
  .command('login')
3377
3388
  .description('User Dev Portal Account to Login')
3378
- .action(async () => {
3379
- await login();
3389
+ .option('--verbose', 'Print verbose logs for debugging')
3390
+ .action(async (cmd) => {
3391
+ await login({ verbose: cmd.verbose });
3392
+ });
3393
+ program
3394
+ .command('setup')
3395
+ .description('Initialize ttmg environment')
3396
+ .option('--lang <lang>', 'Language: en-US | zh-CN')
3397
+ .action(async (cmd) => {
3398
+ await setup({ lang: cmd.lang });
3380
3399
  });
3381
3400
  program
3382
3401
  .option('--h5', 'H5 Mini Game')