@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/README.md +14 -40
- package/dist/index.js +270 -136
- package/dist/index.js.map +1 -1
- package/dist/package.json +2 -2
- package/dist/scripts/verify-agent-contract.js +3 -2
- package/dist/scripts/vibe-ppe-test-preload.cjs +1 -1
- package/dist/scripts/vibe-upload-smoke.js +53 -25
- package/package.json +2 -2
- package/vibe-upload.md +95 -272
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ttmg/cli",
|
|
3
|
-
"version": "0.4.6-vibe-beta.
|
|
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 --
|
|
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 --
|
|
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|
|
|
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
|
|
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'),
|
|
151
|
-
assert.equal(redirect.status,
|
|
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('/
|
|
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
|
|
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
|
-
|
|
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', '
|
|
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, '
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
435
|
-
assert.
|
|
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.
|
|
438
|
-
assert.
|
|
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,
|
|
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,
|
|
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('
|
|
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 =>
|
|
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'), '--
|
|
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.
|
|
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",
|