@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/CHANGELOG.md +556 -0
- package/README.md +3 -3
- package/dist/index.js +108 -22
- package/dist/index.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/scripts/vibe-upload-smoke.js +26 -4
- package/package.json +1 -1
- package/src/commands/common/game/categories.test.ts +38 -0
- package/src/commands/common/game/categories.ts +48 -0
- package/src/commands/common/game/categoryCatalog.json +522 -0
- package/vibe-upload.md +7 -5
package/dist/package.json
CHANGED
|
@@ -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',
|
|
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
|
-
|
|
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
|
|
375
|
+
assert.doesNotMatch(saved.stdout + saved.stderr, /receipt-test-secret/);
|
|
354
376
|
} finally {
|
|
355
377
|
uploadServer.close();
|
|
356
378
|
}
|
package/package.json
CHANGED
|
@@ -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
|
+
}
|