@ttmg/cli 0.4.6-vibe-beta.15 → 0.4.6-vibe-beta.17

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttmg/cli",
3
- "version": "0.4.6-vibe-beta.15",
3
+ "version": "0.4.6-vibe-beta.17",
4
4
  "description": "TikTok Mini Game Command Line Tool",
5
5
  "license": "ISC",
6
6
  "bin": {
@@ -17,7 +17,7 @@
17
17
  "scripts": {
18
18
  "demo:vibe": "npm run build && node scripts/vibe-upload-demo.js",
19
19
  "test:game-categories": "node scripts/run-ts-test.js src/commands/common/game/categories.test.ts",
20
- "test:vibe": "npm run test:game-categories && node scripts/run-ts-test.js src/commands/common/vibe/vibe.test.ts",
20
+ "test:vibe": "npm run test:game-categories && node scripts/run-ts-test.js src/commands/common/vibe/vibe.test.ts && node scripts/run-ts-test.js src/commands/common/vibe/preview.test.ts",
21
21
  "test:vibe-process": "node scripts/vibe-upload-smoke.js",
22
22
  "prepack": "npm run build && npm run verify:dist-version && npm run verify:agent-contract",
23
23
  "build": "node scripts/build.js",
@@ -133,7 +133,8 @@ assert.equal(categoryCatalog.platformWrite, false);
133
133
  assert.equal(categoryCatalog.categoryCount, 12);
134
134
  assert.equal(categoryCatalog.subcategoryCount, 114);
135
135
  assert.equal(capabilities.commands.gameCategories.catalogVersion, categoryCatalog.catalogVersion);
136
- assert.match(capabilities.commands.vibeUpload.command, /--vibe --web/);
136
+ assert.match(capabilities.commands.vibeUpload.command, /--vibe --dir/);
137
+ assert.equal(capabilities.commands.vibeUpload.webAuthorization.available, false);
137
138
  assert.equal(capabilities.commands.vibeUpload.skillWorkflow.keepUploadProcessRunning, true);
138
139
  assert.equal(capabilities.commands.vibeUpload.skillWorkflow.realUploadSuccess.simulation, false);
139
140
  assert.equal(capabilities.commands.vibeUpload.appInfoFields.defaultedByCli.includes('privacy_policy'), true);
@@ -400,7 +401,7 @@ const uploadHelp = runCli(['upload', '--help']);
400
401
  assert.doesNotMatch(uploadHelp.stdout, /--app-info/);
401
402
  assert.match(uploadHelp.stdout.replace(/\s+/g, ' '), /only title and icon|只收集标题和头像/);
402
403
  assertExit(uploadHelp, 0, 'upload help');
403
- assert.match(uploadHelp.stdout, /--vibe --web --dir/);
404
+ assert.match(uploadHelp.stdout, /--vibe --dir/);
404
405
  assert.match(uploadHelp.stdout, /authorizationOpenCommand/);
405
406
  assert.match(uploadHelp.stdout, /simulation=false/);
406
407
  assert.match(uploadHelp.stdout, /--category <category>/);
@@ -10,7 +10,7 @@ https.request = (options, callback) => {
10
10
  assert.equal(typeof options, 'object');
11
11
  assert.equal(options.hostname, 'developers.tiktok.com');
12
12
  assert.equal(options.protocol, 'https:');
13
- assert.match(options.path, /^\/bff\/app_info\/minis_import\/(create_minis_import_task_for_agent|get_minis_import_task_status_for_agent|upload_minis_import_app_icon_for_agent|submit_minis_import_info_for_agent|upload_minis_import_package_for_agent)\//);
13
+ assert.match(options.path, /^\/bff\/app_info\/minis_import\/(create_minis_import_task_for_agent|get_minis_import_task_detail_for_agent|upload_minis_import_app_icon_for_agent|submit_minis_import_info_for_agent|upload_minis_import_package_for_agent)\//);
14
14
  const request = http.request({
15
15
  ...options,
16
16
  protocol: 'http:', hostname: '127.0.0.1', host: undefined, port,
@@ -145,10 +145,10 @@ async function main() {
145
145
  }
146
146
  const launches = fs.readFileSync(browserReceipt, 'utf8').trim().split('\n').map(JSON.parse);
147
147
  assert.equal(launches.length, 2);
148
- assert.ok(launches.every(item => item.args.includes(ready.url + '/authorize')));
148
+ assert.ok(launches.every(item => item.args.includes(ready.url)));
149
149
  const redirect = await fetch(ready.url + '/authorize', {redirect: 'manual'});
150
- assert.equal(redirect.headers.get('location'), landing);
151
- assert.equal(redirect.status, 302);
150
+ assert.equal(redirect.headers.get('location'), null);
151
+ assert.equal(redirect.status, 410);
152
152
  rendered = workerMessage(message => message.type === 'rendered' && message.id === 2);
153
153
  authWorker.send({type: 'render', id: 2, html: '<h1>Complete</h1>'});
154
154
  await rendered;
@@ -171,7 +171,7 @@ async function main() {
171
171
  for await (const chunk of req) chunks.push(chunk);
172
172
  const body = Buffer.concat(chunks);
173
173
  const create = url.pathname.endsWith('/create_minis_import_task_for_agent/');
174
- const status = url.pathname.endsWith('/get_minis_import_task_status_for_agent/');
174
+ const status = url.pathname.endsWith('/get_minis_import_task_detail_for_agent/');
175
175
  const iconUpload = url.pathname.endsWith('/upload_minis_import_app_icon_for_agent/');
176
176
  const infoUpload = url.pathname.endsWith('/submit_minis_import_info_for_agent/');
177
177
  const packageUpload = url.pathname.endsWith('/upload_minis_import_package_for_agent/');
@@ -217,7 +217,11 @@ async function main() {
217
217
  } : iconUpload ? {icon_uri: 'web-icon-receipt'}
218
218
  : infoUpload ? {}
219
219
  : packageUpload ? {status: 3, package_uri: 'web-package-receipt'}
220
- : { status: webMode && webPolls >= 5 ? 2 : 1, ready_for_input: webMode && webPolls >= 5 };
220
+ : { status: webMode && webPolls > 5 ? 6 : webMode && webPolls === 5 ? 2 : 1,
221
+ ready_for_input: webMode && webPolls === 5,
222
+ ...(webMode && webPolls >= 7 ? { preview_url: 'https://www.tiktok.com/minis/process-preview',
223
+ app_id: '7686101169281894417', code_asset_id: '7686101169281894418',
224
+ client_key: 'process-client', minis_center_history_id: '7686101169281894419' } : {}) };
221
225
  res.writeHead(200, { 'Content-Type': 'application/json' });
222
226
  res.end(JSON.stringify({ ...data, error_info: { code: (failStatus && status) || url.pathname === webFailureEndpoint ? 420500 : 0 } }));
223
227
  });
@@ -306,7 +310,7 @@ async function main() {
306
310
  task_id: '7684370037737619477', import_token: 'receipt-test-secret',
307
311
  landing_url: 'https://example.invalid/auth', poll_interval_seconds: 0.01,
308
312
  },
309
- get_minis_import_task_status_for_agent: { status: 2, ready_for_input: true },
313
+ get_minis_import_task_detail_for_agent: { status: 2, ready_for_input: true, preview_url: 'https://www.tiktok.com/minis/receipt-preview' },
310
314
  upload_minis_import_app_icon_for_agent: { icon_uri: 'test-icon' },
311
315
  submit_minis_import_info_for_agent: {},
312
316
  upload_minis_import_package_for_agent: { status: 2, package_uri: 'test-package' },
@@ -328,9 +332,10 @@ async function main() {
328
332
  assert.equal(savedFinal.taskStatusRaw, 2);
329
333
  assert.equal(savedEvents.filter(e => e.terminal).length, 1);
330
334
  assert.deepEqual(uploadPaths, [
331
- 'create_minis_import_task_for_agent', 'get_minis_import_task_status_for_agent',
335
+ 'create_minis_import_task_for_agent', 'get_minis_import_task_detail_for_agent',
332
336
  'upload_minis_import_app_icon_for_agent', 'submit_minis_import_info_for_agent',
333
337
  'upload_minis_import_package_for_agent',
338
+ 'get_minis_import_task_detail_for_agent',
334
339
  ]);
335
340
  assert.doesNotMatch(saved.stdout + saved.stderr, /receipt-test-secret|test-package/);
336
341
  } finally {
@@ -389,7 +394,7 @@ async function main() {
389
394
 
390
395
  const beforeWeb = requests.length;
391
396
  const webConflict = await run([...input, '--web', '--mock', '--format', 'json']);
392
- assert.equal(events(webConflict)[0].errorCode, 'VIBE_WEB_OPTION_CONFLICT');
397
+ assert.equal(events(webConflict)[0].errorCode, 'VIBE_WEB_DISABLED');
393
398
  const legacyWeb = await run(['upload', '--web', '--format', 'json']);
394
399
  assert.equal(events(legacyWeb)[0].errorCode, 'VIBE_OPTION_REQUIRES_VIBE');
395
400
  assert.equal(requests.length, beforeWeb);
@@ -402,7 +407,7 @@ async function main() {
402
407
  [['--category', 'Unknown', '--subcategory', 'Other'], 'VIBE_INVALID_CATEGORY'],
403
408
  [['--category', '', '--subcategory', ''], 'VIBE_INVALID_CATEGORY'],
404
409
  ]) {
405
- const invalid = await run([...input.filter(v => v !== '--no-open'), '--web', ...args, '--format', 'json']);
410
+ const invalid = await run([...input, ...args, '--format', 'json']);
406
411
  assert.equal(invalid.code, 1);
407
412
  const result = events(invalid)[0];
408
413
  assert.equal(result.errorCode, code);
@@ -412,36 +417,46 @@ async function main() {
412
417
  }
413
418
  assert.equal(requests.length, beforeWeb, 'Invalid metadata must not create tasks or upload');
414
419
  webMode = true;
420
+ for (const authorizationMode of ['qr']) {
415
421
  for (const format of ['json', 'ndjson']) {
416
422
  expectedWebDescription = format === 'json' ? 'Discover the fun of TikTok Mini Games.' : 'Stack cats on a rooftop 🐈';
417
423
  const descriptionArgs = format === 'json' ? [] : ['--description', expectedWebDescription];
418
424
  expectedWebCategory = format === 'json' ? {category: 'Other', subcategory: 'Other'} : {category: 'Puzzle', subcategory: 'Physics'};
419
425
  const categoryArgs = format === 'json' ? [] : ['--category', 'Puzzle', '--subcategory', 'Physics'];
420
- const receipt = path.join(temp, `upload-web-${format}.ndjson`);
426
+ const receipt = path.join(temp, `upload-${authorizationMode}-${format}.ndjson`);
421
427
  const requestStart = requests.length;
422
- const web = await run([...input.filter(v => v !== '--no-open'), '--web', ...categoryArgs, ...descriptionArgs, '--format', format, '--poll-interval', '0.2'], async event => {
428
+ const web = await run([...input.filter(v => v !== '--no-open'), ...(authorizationMode === 'web' ? ['--web'] : []), ...categoryArgs, ...descriptionArgs, '--format', format, '--poll-interval', '0.2'], async event => {
423
429
  if (event.status === 'prepared') assert.equal(event.description, expectedWebDescription);
424
430
  if (event.status === 'prepared') assert.deepEqual(
425
431
  {category: event.classification.category, subcategory: event.classification.subcategory},
426
432
  expectedWebCategory);
427
433
  if (event.status === 'waiting_authorization') {
428
- assert.equal(event.authorizationMode, 'web');
434
+ assert.equal(event.authorizationMode, authorizationMode);
429
435
  assert.equal(event.browserOpenRequested, true);
430
436
  assert.equal(event.simulation, false);
431
- assert.equal(event.nextAction, 'open_browser');
432
- assert.equal(event.qrPurpose, undefined);
437
+ assert.equal(event.nextAction, authorizationMode === 'web' ? 'open_browser' : 'scan_qr');
438
+ assert.equal(event.qrPurpose, authorizationMode === 'web' ? undefined : 'authorization_binding');
433
439
  const response = await fetch(event.pageUrl + '/authorize', {redirect: 'manual'});
434
- assert.equal(response.status, 302);
435
- assert.match(response.headers.get('location'), /^https:\/\/developers\.tiktok\.com\/portal_h5\/minis_import\?/);
440
+ assert.equal(response.status, 410);
441
+ assert.equal(response.headers.get('location'), null);
436
442
  const html = await (await fetch(event.pageUrl)).text();
437
- assert.match(html, /Authorize in your browser/);
438
- assert.doesNotMatch(html, /alt="Authorization QR"|Scan the QR code/);
443
+ assert.ok(html.includes(expectedWebDescription));
444
+ assert.ok(html.includes(expectedWebCategory.category));
445
+ assert.ok(html.includes(expectedWebCategory.subcategory));
446
+ assert.doesNotMatch(html, /<header class="brand"/);
447
+ if (authorizationMode === 'web') {
448
+ assert.match(html, /Authorize in your browser/);
449
+ assert.doesNotMatch(html, /alt="Authorization QR"|Scan the QR code/);
450
+ } else {
451
+ assert.match(html, /alt="Authorization QR"/);
452
+ assert.match(html, /data:image\/png;base64/);
453
+ }
439
454
  }
440
455
  }, {
441
456
  NODE_OPTIONS: env.NODE_OPTIONS + ' --require=' + path.join(__dirname, 'vibe-auth-open-test-preload.cjs'),
442
457
  TTMG_AUTH_OPEN_TEST_RECEIPT: receipt,
443
458
  // Explicit --web must not inherit an old Mock backend setting.
444
- TTMG_VIBE_BACKEND: 'mock',
459
+ TTMG_VIBE_BACKEND: authorizationMode === 'web' ? 'mock' : '',
445
460
  TTMG_VIBE_TEST_RETAIN_UPLOAD_SOCKET: '1',
446
461
  });
447
462
  assert.equal(web.code, 0, web.stderr);
@@ -458,24 +473,35 @@ async function main() {
458
473
  assert.equal(output.at(-1).classification.source, format === 'json' ? 'defaults' : 'arguments');
459
474
  assert.equal(output.at(-1).description, expectedWebDescription);
460
475
  assert.equal(output.at(-1).descriptionSource, format === 'json' ? 'defaults' : 'arguments');
461
- assert.equal(output.at(-1).taskStatusRaw, 3);
476
+ assert.equal(output.at(-1).taskStatusRaw, 6);
477
+ assert.equal(output.at(-1).previewReady, true);
478
+ assert.equal(output.at(-1).previewUrl, 'https://www.tiktok.com/minis/process-preview');
479
+ assert.equal(output.at(-1).codeAssetId, '7686101169281894418');
480
+ assert.equal(output.at(-1).qrPurpose, 'game_preview');
481
+ const readyPage = await (await fetch(output.at(-1).pageUrl)).text();
482
+ assert.match(readyPage, /Game preview QR/);
483
+ assert.match(readyPage, /data-phase="preview"/);
462
484
  assert.equal(output.filter(e => e.terminal).length, 1);
463
- assert.equal(webPolls, 5, 'Only true backend readiness unlocks authorization');
485
+ assert.equal(webPolls, 7, 'Keep polling the same task until preview URL is available');
464
486
  assert.deepEqual(requests.slice(requestStart).map(r => r.path.split('/').filter(Boolean).at(-1)), [
465
- 'create_minis_import_task_for_agent', ...Array(5).fill('get_minis_import_task_status_for_agent'),
487
+ 'create_minis_import_task_for_agent', ...Array(5).fill('get_minis_import_task_detail_for_agent'),
466
488
  'upload_minis_import_app_icon_for_agent', 'submit_minis_import_info_for_agent', 'upload_minis_import_package_for_agent',
489
+ 'get_minis_import_task_detail_for_agent', 'get_minis_import_task_detail_for_agent',
467
490
  ]);
468
491
  if (format === 'json') assert.equal(output.length, 1);
469
492
  const opens = fs.readFileSync(receipt, 'utf8').trim().split('\n').map(line => JSON.parse(line));
470
493
  assert.equal(opens.length, 1);
471
- assert.ok(opens[0].args.some(arg => /^http:\/\/127\.0\.0\.1:\d+\/[a-f0-9]{48}\/authorize$/.test(arg)));
494
+ assert.ok(opens[0].args.some(arg => (authorizationMode === 'web'
495
+ ? /^http:\/\/127\.0\.0\.1:\d+\/[a-f0-9]{48}\/authorize$/
496
+ : /^http:\/\/127\.0\.0\.1:\d+\/[a-f0-9]{48}$/).test(arg)));
472
497
  assert.doesNotMatch(JSON.stringify(opens) + web.stdout + web.stderr, /process-test-secret|import_token|web-icon-receipt|web-package-receipt|aweme:\/\//);
473
498
  }
499
+ }
474
500
  expectedWebCategory = {category: 'Other', subcategory: 'Other'};
475
501
  expectedWebDescription = 'Discover the fun of TikTok Mini Games.';
476
502
  webFailureEndpoint = '/bff/app_info/minis_import/submit_minis_import_info_for_agent/';
477
503
  const failedStart = requests.length;
478
- const webUploadFailure = await run([...input.filter(v => v !== '--no-open'), '--web', '--format', 'json'], undefined, {
504
+ const webUploadFailure = await run([...input.filter(v => v !== '--no-open'), '--format', 'json'], undefined, {
479
505
  NODE_OPTIONS: env.NODE_OPTIONS + ' --require=' + path.join(__dirname, 'vibe-auth-open-test-preload.cjs'),
480
506
  TTMG_AUTH_OPEN_TEST_RECEIPT: path.join(temp, 'web-failure-open.ndjson'),
481
507
  TTMG_VIBE_TEST_RETAIN_UPLOAD_SOCKET: '1',
@@ -600,7 +626,9 @@ async function main() {
600
626
  assert.equal(events(projectRun)[0].status, 'prepared');
601
627
  assert.ok(events(projectRun)[0].packageSizeBytes > 0);
602
628
 
603
- const missingBackend = await run([...input, '--format', 'json']);
629
+ const missingBackend = await run([...input, '--format', 'json'], undefined, {
630
+ TTMG_VIBE_BACKEND: 'unsupported',
631
+ });
604
632
  assert.equal(missingBackend.code, 1);
605
633
  assert.equal(
606
634
  events(missingBackend)[0].errorCode,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttmg/cli",
3
- "version": "0.4.6-vibe-beta.15",
3
+ "version": "0.4.6-vibe-beta.17",
4
4
  "description": "TikTok Mini Game Command Line Tool",
5
5
  "license": "ISC",
6
6
  "bin": {
@@ -17,7 +17,7 @@
17
17
  "scripts": {
18
18
  "demo:vibe": "npm run build && node scripts/vibe-upload-demo.js",
19
19
  "test:game-categories": "node scripts/run-ts-test.js src/commands/common/game/categories.test.ts",
20
- "test:vibe": "npm run test:game-categories && node scripts/run-ts-test.js src/commands/common/vibe/vibe.test.ts",
20
+ "test:vibe": "npm run test:game-categories && node scripts/run-ts-test.js src/commands/common/vibe/vibe.test.ts && node scripts/run-ts-test.js src/commands/common/vibe/preview.test.ts",
21
21
  "test:vibe-process": "node scripts/vibe-upload-smoke.js",
22
22
  "prepack": "npm run build && npm run verify:dist-version && npm run verify:agent-contract",
23
23
  "build": "node scripts/build.js",