@ttmg/cli 0.4.6-agent-beta.1 → 0.4.6-agent-beta.2

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-agent-beta.1",
3
+ "version": "0.4.6-agent-beta.2",
4
4
  "description": "TikTok Mini Game Command Line Tool",
5
5
  "license": "ISC",
6
6
  "bin": {
@@ -30,7 +30,8 @@
30
30
  "test:auth": "node scripts/run-ts-test.js src/commands/common/auth.test.ts",
31
31
  "test:capabilities": "node scripts/run-ts-test.js src/commands/common/capabilities.test.ts",
32
32
  "test:test-account": "node scripts/run-ts-test.js src/commands/common/testAccount.test.ts",
33
- "test:dev-session": "node scripts/run-ts-test.js src/commands/native/dev/agentSession.test.ts",
33
+ "test:dev-session": "node scripts/run-ts-test.js src/commands/native/dev/agentSession.test.ts && npm run test:packaging-compatibility",
34
+ "test:packaging-compatibility": "node scripts/run-ts-test.js src/commands/native/dev/packagingCompatibility.test.ts",
34
35
  "test:dev-session-command": "node scripts/run-ts-test.js src/commands/native/dev/session.test.ts && node scripts/run-ts-test.js src/commands/native/dev/projectSession.test.ts && node scripts/run-ts-test.js src/commands/native/dev/scanPage.test.ts && node scripts/run-ts-test.js src/commands/native/dev/runtime/logArchive.test.ts && node scripts/run-ts-test.js src/commands/native/dev/capture.test.ts",
35
36
  "test:dev-runtime-event": "node scripts/run-ts-test.js src/commands/native/dev/utils/scoket.test.ts",
36
37
  "test:dev-runtime-client": "node scripts/run-ts-test.js src/commands/native/dev/runtime/runtime.test.ts",
@@ -93,7 +94,7 @@
93
94
  "qs": "6.12.1",
94
95
  "semver": "^7.7.2",
95
96
  "socks-proxy-agent": "^9.0.0",
96
- "ttmg-pack": "0.4.13-beta.2",
97
+ "ttmg-pack": "0.4.13",
97
98
  "ws": "^8.18.3"
98
99
  },
99
100
  "devDependencies": {
@@ -13,6 +13,10 @@ async function run() {
13
13
  fs.mkdirSync(gameDirectory);
14
14
  fs.writeFileSync(path.join(gameDirectory, 'game.js'), 'console.log("game");\n');
15
15
  fs.writeFileSync(path.join(gameDirectory, 'game.json'), '{}\n');
16
+ for (const directory of ['.ttmg', '.ttmg-assets', 'assets/.ttmg']) {
17
+ fs.mkdirSync(path.join(gameDirectory, directory), { recursive: true });
18
+ fs.writeFileSync(path.join(gameDirectory, directory, 'data.txt'), 'before');
19
+ }
16
20
 
17
21
  let output = '';
18
22
  const child = spawn(
@@ -49,7 +53,24 @@ async function run() {
49
53
  await delay(100);
50
54
  }
51
55
  assert.match(output, /Game resources compiled successfully|游戏资源编译成功/);
52
- assert.equal(fs.existsSync(path.join(gameDirectory, '.ttmg')), false);
56
+ // Ordinary dev must not create an Agent session. Existing evidence must
57
+ // not trigger reloads, while similarly named runtime assets still do.
58
+ assert.equal(fs.existsSync(path.join(gameDirectory, '.ttmg/sessions')), false);
59
+ const compileCount = () => (output.match(/Game resources compiled successfully|游戏资源编译成功/g) || []).length;
60
+ const initialCount = compileCount();
61
+ fs.writeFileSync(path.join(gameDirectory, '.ttmg/data.txt'), 'new log');
62
+ await delay(4500);
63
+ assert.equal(compileCount(), initialCount, 'session writes triggered a rebuild');
64
+ for (const file of ['.ttmg-assets/data.txt', 'assets/.ttmg/data.txt']) {
65
+ const before = compileCount();
66
+ fs.writeFileSync(path.join(gameDirectory, file), 'updated runtime asset');
67
+ const deadline = Date.now() + 15000;
68
+ while (compileCount() === before && Date.now() < deadline) {
69
+ if (child.exitCode !== null) throw new Error(`Default dev exited while watching ${file}:\n${output}`);
70
+ await delay(100);
71
+ }
72
+ assert.ok(compileCount() > before, `${file} did not trigger a rebuild`);
73
+ }
53
74
  } finally {
54
75
  if (child.exitCode === null) {
55
76
  child.kill('SIGTERM');
@@ -10,7 +10,7 @@ const packageJson = JSON.parse(
10
10
  fs.readFileSync(path.join(cliRoot, 'package.json'), 'utf8'),
11
11
  );
12
12
 
13
- function runCli(args, environment = {}) {
13
+ function runCli(args, environment = {}, cwd = cliRoot) {
14
14
  const env = {
15
15
  ...process.env,
16
16
  NO_COLOR: '1',
@@ -20,7 +20,7 @@ function runCli(args, environment = {}) {
20
20
  delete env.TTMG_LOGIN_EMAIL;
21
21
  delete env.TTMG_LOGIN_PASSWORD;
22
22
  return spawnSync(process.execPath, [cliEntry, ...args], {
23
- cwd: cliRoot,
23
+ cwd,
24
24
  env,
25
25
  encoding: 'utf8',
26
26
  });
@@ -270,6 +270,44 @@ assert.equal(parseSingleJson(invalidBackground, 'background').errorCode, 'DEV_IT
270
270
  const invalidManual = runCli(['dev', '--update-mode', 'invalid', '--format', 'json']);
271
271
  assertExit(invalidManual, 1, 'update mode validation');
272
272
  assert.equal(parseSingleJson(invalidManual, 'manual').errorCode, 'DEV_ITERATION_INVALID_OPTIONS');
273
+ // Validation must happen before writing evidence or starting local services.
274
+ const unsafeEvidence = path.join(cliRoot, 'unsafe-evidence-' + process.pid);
275
+ const invalidEvidence = runCli(['dev', '--client-key', 'fixture', '--mode', 'client-preview', '--no-open', '--format', 'json', '--evidence-dir', unsafeEvidence]);
276
+ assertExit(invalidEvidence, 1, 'evidence directory isolation');
277
+ assert.equal(parseSingleJson(invalidEvidence, 'evidence directory').errorCode, 'DEV_EVIDENCE_DIRECTORY_UNSAFE');
278
+ assert.equal(fs.existsSync(unsafeEvidence), false);
279
+ assert.deepEqual(capabilities.commands.dev.sessionLogs.evidenceDirectoryLocations, ['outside-project', 'project-root-.ttmg']);
280
+ const packagingFixture = fs.mkdtempSync(path.join(os.tmpdir(), 'ttmg-installed-pack-gate-'));
281
+ try {
282
+ fs.writeFileSync(path.join(packagingFixture, 'game.js'), 'console.log("fixture");');
283
+ fs.writeFileSync(path.join(packagingFixture, 'game.json'), '{}');
284
+ fs.mkdirSync(path.join(packagingFixture, '.ttmg'));
285
+ fs.writeFileSync(path.join(packagingFixture, '.ttmg/logs.ndjson'), Buffer.alloc(5 * 1024 * 1024));
286
+ fs.writeFileSync(path.join(packagingFixture, '.ttmg/旧构建.js'), 'tt.login();');
287
+ const checked = runCli(['check', '--dir', packagingFixture, '--format', 'json']);
288
+ assertExit(checked, 0, 'installed packaging evidence exclusion');
289
+ const report = parseSingleJson(checked, 'installed packaging evidence exclusion');
290
+ assert.ok(!report.diagnostics.some(item => item.type === 'size' && !item.passed), 'installed ttmg-pack counted CLI evidence in package size');
291
+ assert.ok(report.diagnostics.some(item => item.name === 'login' && !item.passed), 'installed ttmg-pack scanned APIs from old debug builds');
292
+ } finally {
293
+ fs.rmSync(packagingFixture, { recursive: true, force: true });
294
+ }
295
+ if (process.platform !== 'win32') {
296
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ttmg-evidence-symlink-'));
297
+ try {
298
+ fs.mkdirSync(path.join(root, 'assets'));
299
+ fs.writeFileSync(path.join(root, 'game.js'), 'console.log("fixture");');
300
+ fs.symlinkSync(path.join(root, 'assets'), path.join(root, '.ttmg'));
301
+ for (const extra of [[], ['--background']]) {
302
+ const result = runCli(['dev', '--mode', 'client-preview', '--update-mode', 'manual', '--client-key', 'fixture', '--no-open', '--format', 'json', ...extra], {}, root);
303
+ assertExit(result, 1, 'symlinked evidence isolation');
304
+ assert.equal(parseSingleJson(result, 'symlinked evidence').errorCode, 'DEV_EVIDENCE_DIRECTORY_UNSAFE');
305
+ assert.deepEqual(fs.readdirSync(path.join(root, 'assets')), [], 'validation wrote into game assets');
306
+ }
307
+ } finally {
308
+ fs.rmSync(root, { recursive: true, force: true });
309
+ }
310
+ }
273
311
  // Both formats perform the same local build validation; the former text placeholder is retired.
274
312
  const missingBuildDirectory = path.join(os.tmpdir(), 'ttmg-build-dir-that-does-not-exist-' + process.pid);
275
313
  for (const format of ['text', 'json']) {
@@ -0,0 +1,11 @@
1
+ // Test-only browser boundary: never launch a real browser from process smoke.
2
+ const cp = require('node:child_process');
3
+ const fs = require('node:fs');
4
+ const original = cp.spawn;
5
+ cp.spawn = function(command, args, options) {
6
+ if (command === 'open' || /(?:xdg-open|powershell\.exe)$/i.test(command)) {
7
+ fs.appendFileSync(process.env.TTMG_AUTH_OPEN_TEST_RECEIPT, JSON.stringify({command, args}) + '\n');
8
+ return original(process.execPath, ['-e', 'process.exit(0)'], {stdio: 'ignore'});
9
+ }
10
+ return original.call(this, command, args, options);
11
+ };
@@ -99,6 +99,57 @@ function events(result) {
99
99
  .map(line => JSON.parse(line));
100
100
  }
101
101
  async function main() {
102
+ // Exercise the built worker IPC and new command without creating a platform task.
103
+ const authWorker = spawn(process.execPath, [entry, '__vibe-page-server'], {
104
+ env: {...env, TTMG_VIBE_PAGE_TTL_MS: '30000'}, stdio: ['ignore', 'ignore', 'ignore', 'ipc'],
105
+ });
106
+ children.add(authWorker);
107
+ const workerMessage = predicate => new Promise((resolve, reject) => {
108
+ const timer = setTimeout(() => {authWorker.off('message', receive); reject(new Error('worker timeout'));}, 5000);
109
+ function receive(message) {
110
+ if (predicate(message)) {clearTimeout(timer); authWorker.off('message', receive); resolve(message);}
111
+ }
112
+ authWorker.on('message', receive);
113
+ });
114
+ const ready = await workerMessage(message => message.type === 'ready');
115
+ const landing = 'https://developers.tiktok.com/portal_h5/minis_import?task_id=123&import_token=process-private-token';
116
+ let rendered = workerMessage(message => message.type === 'rendered' && message.id === 1);
117
+ authWorker.send({type: 'render', id: 1, html: '<h1>Waiting</h1>', browserLandingUrl: landing});
118
+ await rendered;
119
+ const browserReceipt = path.join(temp, 'browser-open.ndjson');
120
+ const browserEnv = {
121
+ NODE_OPTIONS: `--require=${path.join(__dirname, 'vibe-auth-open-test-preload.cjs')}`,
122
+ TTMG_AUTH_OPEN_TEST_RECEIPT: browserReceipt,
123
+ };
124
+ for (const format of ['json', 'ndjson']) {
125
+ const opened = await run(['auth', 'open', '--page-url', ready.url, '--format', format], undefined, browserEnv);
126
+ assert.equal(opened.code, 0, opened.stderr);
127
+ const result = JSON.parse(opened.stdout);
128
+ assert.equal(result.status, 'open_requested');
129
+ assert.equal(result.platformWrite, false);
130
+ assert.doesNotMatch(opened.stdout + opened.stderr, /import_token|process-private-token/);
131
+ }
132
+ const launches = fs.readFileSync(browserReceipt, 'utf8').trim().split('\n').map(JSON.parse);
133
+ assert.equal(launches.length, 2);
134
+ assert.ok(launches.every(item => item.args.includes(ready.url + '/authorize')));
135
+ const redirect = await fetch(ready.url + '/authorize', {redirect: 'manual'});
136
+ assert.equal(redirect.headers.get('location'), landing);
137
+ assert.equal(redirect.status, 302);
138
+ rendered = workerMessage(message => message.type === 'rendered' && message.id === 2);
139
+ authWorker.send({type: 'render', id: 2, html: '<h1>Complete</h1>'});
140
+ await rendered;
141
+ const stale = await run(['auth', 'open', '--page-url', ready.url, '--format', 'json'], undefined, browserEnv);
142
+ assert.equal(stale.code, 1);
143
+ assert.equal(JSON.parse(stale.stdout).errorCode, 'VIBE_AUTH_PAGE_UNAVAILABLE');
144
+ for (const args of [[], ['--page-url', landing]]) {
145
+ const invalid = await run(['auth', 'open', ...args, '--format', 'json'], undefined, browserEnv);
146
+ assert.equal(invalid.code, 1);
147
+ assert.equal(JSON.parse(invalid.stdout).errorCode, 'VIBE_INVALID_PAGE_URL');
148
+ assert.doesNotMatch(invalid.stdout + invalid.stderr, /process-private-token/);
149
+ }
150
+ assert.equal(fs.readFileSync(browserReceipt, 'utf8').trim().split('\n').length, 2);
151
+ authWorker.kill();
152
+ children.delete(authWorker);
102
153
  fixtureServer = http.createServer((req, res) => {
103
154
  const url = new URL(req.url, 'http://127.0.0.1');
104
155
  requests.push({ path: url.pathname, method: req.method, headers: req.headers });
@@ -381,6 +432,9 @@ async function main() {
381
432
  assert.equal(help.code, 0);
382
433
  assert.match(help.stdout, /--vibe/);
383
434
  assert.match(help.stdout, /--client-key/);
435
+ const authHelp = await run(['auth', 'open', '--help']);
436
+ assert.equal(authHelp.code, 0);
437
+ assert.match(authHelp.stdout, /--page-url/);
384
438
 
385
439
  let cancelledPage;
386
440
  const cancelled = await run(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttmg/cli",
3
- "version": "0.4.6-agent-beta.1",
3
+ "version": "0.4.6-agent-beta.2",
4
4
  "description": "TikTok Mini Game Command Line Tool",
5
5
  "license": "ISC",
6
6
  "bin": {
@@ -30,7 +30,8 @@
30
30
  "test:auth": "node scripts/run-ts-test.js src/commands/common/auth.test.ts",
31
31
  "test:capabilities": "node scripts/run-ts-test.js src/commands/common/capabilities.test.ts",
32
32
  "test:test-account": "node scripts/run-ts-test.js src/commands/common/testAccount.test.ts",
33
- "test:dev-session": "node scripts/run-ts-test.js src/commands/native/dev/agentSession.test.ts",
33
+ "test:dev-session": "node scripts/run-ts-test.js src/commands/native/dev/agentSession.test.ts && npm run test:packaging-compatibility",
34
+ "test:packaging-compatibility": "node scripts/run-ts-test.js src/commands/native/dev/packagingCompatibility.test.ts",
34
35
  "test:dev-session-command": "node scripts/run-ts-test.js src/commands/native/dev/session.test.ts && node scripts/run-ts-test.js src/commands/native/dev/projectSession.test.ts && node scripts/run-ts-test.js src/commands/native/dev/scanPage.test.ts && node scripts/run-ts-test.js src/commands/native/dev/runtime/logArchive.test.ts && node scripts/run-ts-test.js src/commands/native/dev/capture.test.ts",
35
36
  "test:dev-runtime-event": "node scripts/run-ts-test.js src/commands/native/dev/utils/scoket.test.ts",
36
37
  "test:dev-runtime-client": "node scripts/run-ts-test.js src/commands/native/dev/runtime/runtime.test.ts",
@@ -93,7 +94,7 @@
93
94
  "qs": "6.12.1",
94
95
  "semver": "^7.7.2",
95
96
  "socks-proxy-agent": "^9.0.0",
96
- "ttmg-pack": "0.4.13-beta.2",
97
+ "ttmg-pack": "0.4.13",
97
98
  "ws": "^8.18.3"
98
99
  },
99
100
  "devDependencies": {