@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/CHANGELOG.md +5 -0
- package/dist/index.js +276 -257
- package/dist/index.js.map +1 -1
- package/dist/package.json +4 -3
- package/dist/public/assets/index-BF340A10.js +67 -0
- package/dist/public/assets/index-BF340A10.js.br +0 -0
- package/dist/public/assets/index-BrY3F5Qq.js +23 -0
- package/dist/public/assets/index-BrY3F5Qq.js.br +0 -0
- package/dist/public/assets/index-C3n9EBdl.css +1 -0
- package/dist/public/assets/index-C3n9EBdl.css.br +0 -0
- package/dist/public/assets/index-CAUBU0xh.js +67 -0
- package/dist/public/assets/index-CAUBU0xh.js.br +0 -0
- package/dist/public/assets/index-CdIYjcoE.js +23 -0
- package/dist/public/assets/index-CdIYjcoE.js.br +0 -0
- package/dist/public/assets/index-Dkp1qLZg.js +23 -0
- package/dist/public/assets/index-Dkp1qLZg.js.br +0 -0
- package/dist/public/assets/index-TTMMHmbX.js +22 -0
- package/dist/public/assets/index-TTMMHmbX.js.br +0 -0
- package/dist/public/index.html +2 -2
- package/dist/scripts/build.js +10 -0
- package/dist/scripts/setup.js +30 -0
- package/package.json +4 -3
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
|
-
|
|
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
|
-
|
|
355
|
-
|
|
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
|
-
|
|
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
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
'
|
|
378
|
-
|
|
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
|
-
|
|
479
|
-
|
|
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
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
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,
|
|
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
|
|
564
|
-
|
|
565
|
-
|
|
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' }),
|
|
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
|
-
|
|
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
|
|
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 =
|
|
2404
|
-
|
|
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
|
|
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
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
.
|
|
3379
|
-
|
|
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')
|