@ttmg/cli 0.4.6-vibe-beta.22 → 0.4.6-vibe-beta.24

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.22",
3
+ "version": "0.4.6-vibe-beta.24",
4
4
  "description": "TikTok Mini Game Command Line Tool",
5
5
  "license": "ISC",
6
6
  "bin": {
@@ -319,7 +319,8 @@ async function main() {
319
319
  const replies = {
320
320
  create_minis_import_task_for_agent: {
321
321
  task_id: '7684370037737619477', import_token: 'receipt-test-secret',
322
- landing_url: 'https://example.invalid/auth', poll_interval_seconds: 0.01,
322
+ landing_url: 'https://example.invalid/auth?task_id=7684370037737619477&import_token=receipt-test-secret',
323
+ poll_interval_seconds: 0.01,
323
324
  },
324
325
  get_minis_import_task_detail_for_agent: { status: 2, ready_for_input: true, preview_url: 'https://www.tiktok.com/minis/receipt-preview' },
325
326
  upload_minis_import_app_icon_for_agent: { icon_uri: 'test-icon' },
@@ -331,14 +332,35 @@ async function main() {
331
332
  });
332
333
  await new Promise(resolve => uploadServer.listen(0, '127.0.0.1', resolve));
333
334
  try {
334
- const saved = await run([...input, '--format', 'ndjson'], undefined, {
335
+ const saved = await run([...input, '--format', 'ndjson', '--verbose'], undefined, {
335
336
  TTMG_VIBE_BACKEND: 'local-http',
336
337
  TTMG_VIBE_API: `http://127.0.0.1:${uploadServer.address().port}`,
337
338
  });
338
339
  assert.equal(saved.code, 0, saved.stderr);
339
340
  const savedEvents = events(saved);
340
341
  const waitingPage = savedEvents.find(e => e.status === 'waiting_authorization').pageUrl;
341
- assert.deepEqual(saved.stderr.split(/\r?\n/).filter(line => line.startsWith('page: ')), [`page: ${waitingPage}`]);
342
+ const savedStderr = saved.stderr.split(/\r?\n/).filter(Boolean);
343
+ assert.deepEqual(savedStderr.filter(line => line.startsWith('page: ')), [`page: ${waitingPage}`]);
344
+ const httpTraces = savedStderr.filter(line => line.startsWith('{')).map(JSON.parse);
345
+ assert.deepEqual(httpTraces.map(trace => trace.event), [
346
+ 'http_request', 'http_response',
347
+ 'http_request', 'http_response',
348
+ 'http_request', 'http_response',
349
+ 'http_request', 'http_response',
350
+ 'http_request', 'http_response',
351
+ 'http_request', 'http_response',
352
+ ]);
353
+ const createRequest = httpTraces.find(trace => trace.event === 'http_request' && trace.url.endsWith('/create_minis_import_task_for_agent/'));
354
+ const createResponse = httpTraces.find(trace => trace.event === 'http_response' && trace.url.endsWith('/create_minis_import_task_for_agent/'));
355
+ assert.equal(createRequest.request.source_project_id.length, 36);
356
+ assert.equal(createRequest.request.task_idempotency_id.length, 36);
357
+ assert.equal(createResponse.response.task_id, '7684370037737619477');
358
+ assert.equal(createResponse.response.import_token, '[REDACTED]');
359
+ assert.match(createResponse.response.landing_url, /import_token=%5BREDACTED%5D/);
360
+ const pollRequests = httpTraces.filter(trace => trace.event === 'http_request' && trace.url.endsWith('/get_minis_import_task_detail_for_agent/'));
361
+ assert.ok(pollRequests.length >= 2);
362
+ assert.ok(pollRequests.every(trace => trace.request.task_id === '7684370037737619477' && trace.request.import_token === '[REDACTED]'));
363
+ assert.doesNotMatch(saved.stderr, /receipt-test-secret/);
342
364
  const savedFinal = savedEvents.at(-1);
343
365
  assert.equal(savedFinal.status, 'uploaded', JSON.stringify(savedEvents.map(e => ({ status: e.status, terminal: e.terminal, step: e.uploadStep, uploaded: e.uploaded }))));
344
366
  assert.equal(savedFinal.uploaded, true);
@@ -350,7 +372,7 @@ async function main() {
350
372
  'upload_minis_import_package_for_agent',
351
373
  'get_minis_import_task_detail_for_agent',
352
374
  ]);
353
- assert.doesNotMatch(saved.stdout + saved.stderr, /receipt-test-secret|test-package/);
375
+ assert.doesNotMatch(saved.stdout + saved.stderr, /receipt-test-secret/);
354
376
  } finally {
355
377
  uploadServer.close();
356
378
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttmg/cli",
3
- "version": "0.4.6-vibe-beta.22",
3
+ "version": "0.4.6-vibe-beta.24",
4
4
  "description": "TikTok Mini Game Command Line Tool",
5
5
  "license": "ISC",
6
6
  "bin": {
@@ -0,0 +1,38 @@
1
+ import assert from 'node:assert/strict';
2
+ import { createHash } from 'crypto';
3
+ import { GAME_CATEGORIES, GAME_CATEGORY_CATALOG_VERSION, getGameCategories, isGameCategoryPair } from './categories';
4
+ import { applyVibeCategory, VIBE_PPE_DEFAULT_APP_INFO } from '../vibe/appInfo';
5
+
6
+ const result = getGameCategories({ format: 'json' });
7
+ assert.equal(result.platformWrite, false);
8
+ assert.equal(result.categoryCount, 12);
9
+ assert.equal(result.subcategoryCount, 114);
10
+ assert.equal(result.errorCode, null);
11
+ assert.equal(GAME_CATEGORY_CATALOG_VERSION, 'sha256:' + createHash('sha256').update(JSON.stringify(GAME_CATEGORIES)).digest('hex'));
12
+ assert.equal(new Set(GAME_CATEGORIES.map(item => item.value)).size, 12);
13
+ for (const category of GAME_CATEGORIES) {
14
+ assert.equal(new Set(category.subcategories.map(item => item.value)).size, category.subcategories.length);
15
+ for (const item of category.subcategories) assert.ok(item.description.trim());
16
+ }
17
+ assert.deepEqual(getGameCategories({ category: 'Puzzle' }).categories.map(item => item.value), ['Puzzle']);
18
+ assert.equal(getGameCategories({ category: 'puzzle' }).errorCode, 'GAME_CATEGORY_UNKNOWN');
19
+ assert.equal(getGameCategories({ category: '' }).errorCode, 'GAME_CATEGORY_UNKNOWN');
20
+ assert.equal(getGameCategories({ format: 'xml' }).errorCode, 'GAME_CATEGORIES_INVALID_FORMAT');
21
+ assert.ok(isGameCategoryPair('Action', "Shoot'em Up"));
22
+ assert.ok(isGameCategoryPair('Action', "Beat 'em up"));
23
+ assert.ok(!isGameCategoryPair('Other', 'Physics'));
24
+ assert.ok(!isGameCategoryPair('Casino', 'Bingo'));
25
+
26
+ assert.equal(applyVibeCategory({}), undefined);
27
+ const legacy = { ...VIBE_PPE_DEFAULT_APP_INFO, category: 'Casual', subcategory: 'Puzzle', description: 'Legacy description' };
28
+ assert.equal(applyVibeCategory({}, legacy), legacy, 'Old file callers remain unchanged when new flags are absent');
29
+ const pair = { category: 'Puzzle', subcategory: 'Physics' };
30
+ assert.deepEqual(applyVibeCategory(pair), { ...VIBE_PPE_DEFAULT_APP_INFO, ...pair });
31
+ assert.deepEqual(applyVibeCategory(pair, legacy), { ...legacy, ...pair });
32
+ assert.equal(VIBE_PPE_DEFAULT_APP_INFO.category, 'Other');
33
+ assert.equal(legacy.category, 'Casual', 'Overrides must not mutate old input');
34
+ for (const options of [{category:'Puzzle'}, {subcategory:'Physics'}])
35
+ assert.throws(() => applyVibeCategory(options), /VIBE_CATEGORY_PAIR_REQUIRED/);
36
+ for (const options of [{category:'Other',subcategory:'Physics'}, {category:'puzzle',subcategory:'Physics'}, {category:'',subcategory:''}])
37
+ assert.throws(() => applyVibeCategory(options), /VIBE_INVALID_CATEGORY/);
38
+ console.log('game category catalog and compatibility tests passed');
@@ -0,0 +1,48 @@
1
+ import { randomUUID } from 'crypto';
2
+ import catalog from './categoryCatalog.json';
3
+ import { t } from '../../../libs/i18n';
4
+
5
+ export const GAME_CATEGORY_CATALOG_VERSION = catalog.catalogVersion;
6
+ export const GAME_CATEGORIES = catalog.categories;
7
+
8
+ export function isGameCategoryPair(category: string, subcategory: string) {
9
+ return GAME_CATEGORIES.some(item => item.value === category &&
10
+ item.subcategories.some(child => child.value === subcategory));
11
+ }
12
+
13
+ export function getGameCategories(options: { category?: string; format?: string } = {}) {
14
+ const errorCode = !['text', 'json', 'ndjson'].includes(options.format || 'text')
15
+ ? 'GAME_CATEGORIES_INVALID_FORMAT'
16
+ : options.category !== undefined && !GAME_CATEGORIES.some(item => item.value === options.category)
17
+ ? 'GAME_CATEGORY_UNKNOWN' : null;
18
+ const categories = errorCode ? [] : GAME_CATEGORIES.filter(item =>
19
+ options.category === undefined || item.value === options.category);
20
+ return {
21
+ schemaVersion: '1',
22
+ command: 'game categories',
23
+ operationId: `game_categories_${randomUUID()}`,
24
+ status: errorCode ? 'failed' : 'ready',
25
+ terminal: true,
26
+ platformWrite: false,
27
+ catalogVersion: GAME_CATEGORY_CATALOG_VERSION,
28
+ source: catalog.source,
29
+ descriptionLanguage: 'en',
30
+ categoryCount: categories.length,
31
+ subcategoryCount: categories.reduce((count, item) => count + item.subcategories.length, 0),
32
+ categories,
33
+ errorCode,
34
+ error: errorCode ? t(errorCode === 'GAME_CATEGORY_UNKNOWN'
35
+ ? 'cli.command.game.categories.unknown' : 'cli.command.game.categories.invalidFormat') : null,
36
+ };
37
+ }
38
+
39
+ export function gameCategories(options: { category?: string; format?: string }) {
40
+ const result = getGameCategories(options);
41
+ if (options.format && options.format !== 'text') console.log(JSON.stringify(result));
42
+ else if (result.error) console.error(result.error);
43
+ else for (const category of result.categories) {
44
+ console.log(category.value);
45
+ for (const item of category.subcategories) console.log(` ${item.value}: ${item.description}`);
46
+ }
47
+ if (result.errorCode) process.exitCode = 1;
48
+ }