@ttmg/cli 0.4.6-vibe-beta.16 → 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.16",
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",
@@ -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,
@@ -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 {
@@ -468,12 +473,20 @@ async function main() {
468
473
  assert.equal(output.at(-1).classification.source, format === 'json' ? 'defaults' : 'arguments');
469
474
  assert.equal(output.at(-1).description, expectedWebDescription);
470
475
  assert.equal(output.at(-1).descriptionSource, format === 'json' ? 'defaults' : 'arguments');
471
- 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"/);
472
484
  assert.equal(output.filter(e => e.terminal).length, 1);
473
- 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');
474
486
  assert.deepEqual(requests.slice(requestStart).map(r => r.path.split('/').filter(Boolean).at(-1)), [
475
- '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'),
476
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',
477
490
  ]);
478
491
  if (format === 'json') assert.equal(output.length, 1);
479
492
  const opens = fs.readFileSync(receipt, 'utf8').trim().split('\n').map(line => JSON.parse(line));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttmg/cli",
3
- "version": "0.4.6-vibe-beta.16",
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",
package/vibe-upload.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  `ttmg upload --vibe` 将本地小游戏上传到 TikTok Mini Games。用户提供游戏标题和头像,在手机 TikTok 扫码授权后,CLI 自动完成头像、应用资料和代码包上传。Skill 负责准备工程、调用命令和读取结果;CLI 负责素材校验、打包、授权轮询及上传。
5
5
 
6
- 适用版本:`0.4.6-vibe-beta.16`。手机授权与真实上传已使用 PPE 测试素材验证;正式环境未开放。上传完成不代表审核通过或正式发布。
6
+ `0.4.6-vibe-beta.17` 支持手机授权、上传后持续轮询和扫码预览。Snow Crew 已在 PPE 完成真实上传并取得预览链接,用户确认扫码预览跑通。正式环境未开放,上传完成不代表审核通过或正式发布。
7
7
 
8
8
  ## Quick Start
9
9
 
@@ -18,18 +18,23 @@ ttmg upload --vibe --dir ./game --title "星环守卫" --icon ./avatar.png --for
18
18
  ```
19
19
 
20
20
  1. 启动命令,CLI 检查素材并自动打开电脑上的二维码页。
21
+
21
22
  2. 用户使用手机 TikTok 扫码授权,保持原命令运行。
23
+
22
24
  3. CLI 自动上传头像、应用资料和代码包,页面展示进度与结果。
23
- 4. Skill 收到 `terminal=true`、`status=uploaded`、`uploaded=true`、`simulation=false` 且退出码为 0 后,报告上传完成。
25
+
26
+ 4. 上传后保持原进程运行,CLI 继续查询同一任务;拿到 `previewUrl` 后,原页面自动显示游戏预览二维码,使用手机 TikTok 扫码预览。
27
+
28
+ 5. Skill 收到 `terminal=true`、`status=uploaded`、`uploaded=true`、`previewReady=true`、`simulation=false` 且退出码为 0 后,报告上传完成且可扫码预览。
24
29
 
25
30
  已有 ZIP 时,用 `--package ./game.zip` 替换 `--dir ./game`。无需预先执行 `ttmg login` 或配置 PPE 环境。描述和分类可省略,CLI 使用默认资料;完整参数见下文。
26
31
 
27
32
  ## 安装与本地预检
28
33
 
29
- 安装后确认版本为 `0.4.6-vibe-beta.16`,能力查询中 `authorizationMode` 为 `qr`。支持 Node.js 20.19.1–20.x 或 22 及以上版本。
34
+ 安装后确认版本为 `0.4.6-vibe-beta.17`,能力查询中 `authorizationMode` 为 `qr`、`previewQr` 为 `true`。支持 Node.js 20.19.1–20.x 或 22 及以上版本。
30
35
 
31
36
  ```sh
32
- npm install -g @ttmg/cli@0.4.6-vibe-beta.16
37
+ npm install -g @ttmg/cli@0.4.6-vibe-beta.17
33
38
  ttmg --version
34
39
  ttmg capabilities --format json
35
40
  ```
@@ -113,7 +118,8 @@ ttmg upload --vibe --dir ./game --title "星环守卫" --icon ./avatar.png --cat
113
118
 
114
119
  3. 收到 `uploading`,按 `uploadStep` 展示 `avatar`、`app_info`、`package` 进度。授权轮询和上传由 CLI 完成。
115
120
 
116
- 4. 收到 `terminal=true`、`status=uploaded`、`uploaded=true`、`simulation=false`,确认真实上传完成。`failed`、`cancelled` 或进程中断按下方失败说明处理。
121
+ 4. 收到 `waiting_preview` 时,素材已上传,保持原进程运行。CLI 使用同一任务继续查询;状态已完成但尚无预览链接时也继续等待。
122
+ 5. 终态仍为 `status=uploaded`。`previewReady=true`、`qrPurpose=game_preview` 表示预览二维码已就绪,`previewUrl` 是二维码内容,`pageUrl` 是展示二维码的本地页面。等待失败时保留 `uploaded=true`,同时返回错误和 `nextAction=check_task`,不要重新上传。
117
123
 
118
124
  ## 扫码授权与自动上传
119
125
 
@@ -147,7 +153,8 @@ ttmg auth open --page-url <pageUrl> --format json
147
153
 
148
154
  - `uploading`:根据 uploadStep 显示 avatar、app_info、package 的进度,不再询问授权。
149
155
 
150
- - `uploaded`:terminal=true、uploaded=true、simulation=false 且退出码 0:本次上传完成;不是审核通过或正式发布。
156
+ - `waiting_preview`:uploaded=true、terminal=false,正在查询后端处理结果,等待预览链接。
157
+ - `uploaded`:terminal=true、uploaded=true、simulation=false 且退出码 0:上传完成,previewReady=true 时可扫码预览;不是审核通过或正式发布。
151
158
 
152
159
  - `failed / cancelled`:根据 errorCode、message 和 nextAction 提示处理,不自动重传。
153
160
 
@@ -158,15 +165,17 @@ ttmg auth open --page-url <pageUrl> --format json
158
165
 
159
166
  按阶段补充:`taskId`(字符串,禁止转成 JS Number)、`pollIntervalSeconds`、`readyForInput`、`taskStatusRaw`(仅诊断)、`uploadStep`、`loadedBytes`、`totalBytes`、`pageUrl`、`qrPurpose=authorization_binding`、素材大小和包 SHA256。代码包字节进度达到 100% 不代表成功,必须等待最终上传回执。
160
167
 
161
- `nextAction` 取值:`wait`、`scan_qr`、`fix_input`、`restart_authorization`、`check_task`、`none`。已开始上传后发生失败/超时,先检查 task,不能直接再次创建并重传。若上传已完成但页面失败,仍保留 `uploaded=true`,同时返回错误;Skill 提示上传已完成但展示失败。
168
+ `nextAction` 取值:`wait`、`scan_qr`、`fix_input`、`restart_authorization`、`check_task`、`none`。已开始上传后发生失败/超时,先检查 task,不能直接再次创建并重传。预览等待超时返回 `VIBE_PREVIEW_TIMEOUT`;查询失败、取消或页面失败均保留 `uploaded=true`,并返回错误及 `nextAction=check_task`。
169
+
170
+ 新结果字段:`previewUrl`、`previewReady`、`clientKey`、`appId`、`codeAssetId`、`minisCenterHistoryId`。业务 ID 使用字符串,后端尚未返回时省略;只有有效的非空 preview_url 才触发预览就绪。Mock 保持模拟上传终态,不返回真实预览二维码。
162
171
 
163
172
  退出码:成功/dry-run 为 0,失败为 1,Ctrl+C 为 130。进程或宿主意外终止导致没有终态时,不得认定上传成功。
164
173
 
165
- 轮询间隔使用创建任务返回的 `poll_interval_seconds`。`--timeout` 是整体超时,默认 300 秒。不自动重试写接口、不实现跨进程续传。
174
+ 轮询间隔使用创建任务返回的 `poll_interval_seconds`。`--timeout` 是包括授权、上传和等待预览的整体超时,默认 300 秒。不自动重试写接口、不实现跨进程续传。
166
175
 
167
176
  ## 页面与素材
168
177
 
169
- 本地页在扫码、上传、成功或失败时均展示本次资料。后台查询状态,只更新变化内容,不整页刷新;完成或失败后停止查询。授权完成或失败后移除二维码。Skill 可用 `pageUrl` 重新打开同一页面。
178
+ 本地页在扫码、上传、等待预览、成功或失败时均展示本次资料。后台查询状态,只更新变化内容,不整页刷新;预览就绪或终态失败后停止查询。授权完成后移除授权二维码;preview_url 就绪后显示游戏预览二维码,直接使用后端 URL。Skill 可用 `pageUrl` 重新打开同一页面。
170
179
 
171
180
  页面只监听 127.0.0.1,二维码才由手机扫描,手机不访问 pageUrl。默认 15 分钟后关闭,内容驻留内存;测试可设置 `TTMG_VIBE_PAGE_TTL_MS`(1—900000)。取消进程会关闭页面;成功与可显示的失败页面保留至到期。不要分享本地页面地址。
172
181
 
@@ -182,7 +191,7 @@ CLI 在工程目录的 `.ttmg/vibe-project.json` 保存并复用非敏感 source
182
191
 
183
192
  - `POST create_minis_import_task_for_agent/`:请求 source_type=1、source_project_id、task_idempotency_id、minis_type=MINI_GAME。响应 task_id、import_token、landing_url、poll_interval_seconds。
184
193
 
185
- - `GET get_minis_import_task_status_for_agent/`:请求 Query:task_id、import_token。响应 status、ready_for_input。
194
+ - `GET get_minis_import_task_detail_for_agent/`:请求 Query:task_id、import_token。响应 status、ready_for_input,以及可选的 client_key、app_id、code_asset_id、minis_center_history_id、preview_url;授权和上传后的轮询均使用此接口。
186
195
 
187
196
  - `POST upload_minis_import_app_icon_for_agent/`:请求 Multipart:task_id、import_token、file。响应 icon_uri。
188
197
 
@@ -191,7 +200,7 @@ CLI 在工程目录的 `.ttmg/vibe-project.json` 保存并复用非敏感 source
191
200
  - `POST upload_minis_import_package_for_agent/`:请求 Multipart:task_id、import_token、asset_type=2、file。响应 package_uri、status。
192
201
 
193
202
 
194
- 所有响应必须业务 `error_info.code=0`。最后一步还要求非空 package_uri 和合法非失败状态;完成后不再轮询。HTTP 200、进度100% 都不能单独作为成功判定。19位 task_id 在 CLI 内保留字符串,提交 i64 JSON 时无损编码。
203
+ 所有响应必须业务 `error_info.code=0`。上传回执还要求非空 package_uri 和合法非失败状态;收到回执后继续查询 Detail,直到拿到有效 preview_url。HTTP 200、进度100% 都不能单独作为成功判定。19位 task_id 在 CLI 内保留字符串,提交 i64 JSON 时无损编码。任务凭据只在当前进程内保留,终态后清除。
195
204
 
196
205
  ```sh
197
206
  TTMG_VIBE_BACKEND=local-http TTMG_VIBE_API=http://127.0.0.1:3100 ttmg upload --vibe --package ./game.zip --title "测试游戏" --icon ./avatar.png --format ndjson
@@ -199,7 +208,9 @@ TTMG_VIBE_BACKEND=local-http TTMG_VIBE_API=http://127.0.0.1:3100 ttmg upload --v
199
208
 
200
209
  维护者可使用 `--vibe-api http://127.0.0.1:3100` 验证本机 HTTP 合同,只接受回环 IP,不跟随重定向、不读取 Cookie;本地服务需实现上表协议。Skill 使用 `--vibe`,PPE 路由由 CLI 内置。正式环境未开放。
201
210
 
202
- **v1 → v2 兼容性变化**:旧 UUID 换 Token 的三接口协议废弃,`--vibe-authorize-url` 返回明确错误;授权地址来自 landing_url。不再输出 preview_ready、game_preview、uploadId、reviewStatus。Skill 必须校验 schemaVersion=2,并按新终态处理。普通不带 --vibe 的 upload 不变。
211
+ **v1 → v2 兼容性变化**:旧 UUID 换 Token 的三接口协议废弃,`--vibe-authorize-url` 返回明确错误;授权地址来自 landing_url。不再输出旧 preview_ready 状态、uploadId、reviewStatus。
212
+
213
+ 当前版本新增 waiting_preview 状态,终态保留 uploaded;game_preview 作为预览二维码用途。Skill 必须校验 schemaVersion=2,并按终态、previewReady 和 errorCode 处理。普通不带 --vibe 的 upload 不变。
203
214
 
204
215
  2026-09-14 已核对 BFF 源码:头像 PNG/JPEG、1024×1024、5 MiB;Native ZIP 和解压总大小上限 100 MiB。本地暂未实现完整 ZIP 内容及解压总大小校验。2026-09-16 本地构建使用自有测试素材跑通真实 PPE 手机扫码授权、头像、默认资料和代码包上传。该验证不覆盖全部游戏或正式环境。后端接口调整留在适配层处理;若用户动作或成功语义改变,应协商升级 CLI schema。
205
216