@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/README.md +6 -12
- package/dist/index.js +119 -40
- package/dist/index.js.map +1 -1
- package/dist/package.json +2 -2
- package/dist/scripts/vibe-ppe-test-preload.cjs +1 -1
- package/dist/scripts/vibe-upload-smoke.js +20 -7
- package/package.json +2 -2
- package/vibe-upload.md +23 -12
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",
|
|
@@ -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,
|
|
@@ -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 {
|
|
@@ -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,
|
|
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,
|
|
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('
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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. 收到 `
|
|
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
|
-
- `
|
|
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
|
|
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`
|
|
174
|
+
轮询间隔使用创建任务返回的 `poll_interval_seconds`。`--timeout` 是包括授权、上传和等待预览的整体超时,默认 300 秒。不自动重试写接口、不实现跨进程续传。
|
|
166
175
|
|
|
167
176
|
## 页面与素材
|
|
168
177
|
|
|
169
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|