@ttmg/cli 0.4.6-vibe-beta.18 → 0.4.6-vibe-beta.19
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 +13 -5
- package/dist/index.js +456 -83
- package/dist/index.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/scripts/vibe-upload-smoke.js +21 -6
- package/package.json +1 -1
- package/vibe-upload.md +25 -14
package/dist/package.json
CHANGED
|
@@ -115,7 +115,7 @@ function events(result) {
|
|
|
115
115
|
async function main() {
|
|
116
116
|
// Exercise the built worker IPC and new command without creating a platform task.
|
|
117
117
|
const authWorker = spawn(process.execPath, [entry, '__vibe-page-server'], {
|
|
118
|
-
env: {...env, TTMG_VIBE_PAGE_TTL_MS: '
|
|
118
|
+
env: {...env, TTMG_VIBE_PAGE_TTL_MS: '500'}, stdio: ['ignore', 'ignore', 'ignore', 'ipc'],
|
|
119
119
|
});
|
|
120
120
|
children.add(authWorker);
|
|
121
121
|
const workerMessage = predicate => new Promise((resolve, reject) => {
|
|
@@ -130,6 +130,9 @@ async function main() {
|
|
|
130
130
|
let rendered = workerMessage(message => message.type === 'rendered' && message.id === 1);
|
|
131
131
|
authWorker.send({type: 'render', id: 1, html: '<h1>Waiting</h1>', browserLandingUrl: landing});
|
|
132
132
|
await rendered;
|
|
133
|
+
await new Promise(resolve => setTimeout(resolve, 700));
|
|
134
|
+
assert.equal(await (await fetch(ready.url)).text(), '<h1>Waiting</h1>',
|
|
135
|
+
'Retention TTL must not close the page while the CLI is still polling');
|
|
133
136
|
const browserReceipt = path.join(temp, 'browser-open.ndjson');
|
|
134
137
|
const browserEnv = {
|
|
135
138
|
NODE_OPTIONS: `--require=${path.join(__dirname, 'vibe-auth-open-test-preload.cjs')}`,
|
|
@@ -162,7 +165,15 @@ async function main() {
|
|
|
162
165
|
assert.doesNotMatch(invalid.stdout + invalid.stderr, /process-private-token/);
|
|
163
166
|
}
|
|
164
167
|
assert.equal(fs.readFileSync(browserReceipt, 'utf8').trim().split('\n').length, 2);
|
|
165
|
-
|
|
168
|
+
const workerExited = new Promise((resolve, reject) => {
|
|
169
|
+
const timer = setTimeout(() => reject(new Error('Detached worker did not expire')), 4000);
|
|
170
|
+
authWorker.once('exit', code => { clearTimeout(timer); resolve(code); });
|
|
171
|
+
});
|
|
172
|
+
authWorker.disconnect();
|
|
173
|
+
assert.equal(await (await fetch(ready.url)).text(), '<h1>Complete</h1>',
|
|
174
|
+
'A completed page remains readable after CLI detaches');
|
|
175
|
+
assert.equal(await workerExited, 0);
|
|
176
|
+
await assert.rejects(fetch(ready.url), 'Detached page must close when retention expires');
|
|
166
177
|
children.delete(authWorker);
|
|
167
178
|
fixtureServer = http.createServer(async (req, res) => {
|
|
168
179
|
const url = new URL(req.url, 'http://127.0.0.1');
|
|
@@ -390,7 +401,10 @@ async function main() {
|
|
|
390
401
|
const previewHtml = await (await fetch(final.pageUrl)).text();
|
|
391
402
|
assert.match(previewHtml, /Simulated upload complete/);
|
|
392
403
|
assert.match(previewHtml, /data-phase="complete"/);
|
|
393
|
-
assert.doesNotMatch(previewHtml, /QR|preview|review|Publish to TikTok/);
|
|
404
|
+
assert.doesNotMatch(previewHtml.split('<body>')[1], /QR|preview|review|Publish to TikTok/);
|
|
405
|
+
const localizedPage = await fetch(final.pageUrl, {headers: {'Accept-Language': 'zh-CN'}});
|
|
406
|
+
assert.equal(localizedPage.headers.get('vary'), 'Accept-Language');
|
|
407
|
+
assert.match(await localizedPage.text(), /<html lang="zh-CN">[\s\S]*模拟上传完成/);
|
|
394
408
|
|
|
395
409
|
const beforeWeb = requests.length;
|
|
396
410
|
const webConflict = await run([...input, '--web', '--mock', '--format', 'json']);
|
|
@@ -477,10 +491,11 @@ async function main() {
|
|
|
477
491
|
assert.equal(output.at(-1).previewReady, true);
|
|
478
492
|
assert.equal(output.at(-1).previewUrl, 'https://www.tiktok.com/minis/process-preview');
|
|
479
493
|
assert.equal(output.at(-1).codeAssetId, '7686101169281894418');
|
|
480
|
-
assert.equal(output.at(-1).qrPurpose, '
|
|
494
|
+
assert.equal(output.at(-1).qrPurpose, 'game_preview');
|
|
481
495
|
const readyPage = await (await fetch(output.at(-1).pageUrl)).text();
|
|
482
|
-
assert.match(readyPage, /
|
|
483
|
-
assert.
|
|
496
|
+
assert.match(readyPage, /Progress QR/);
|
|
497
|
+
assert.match(readyPage, /Game preview QR/);
|
|
498
|
+
assert.ok(readyPage.indexOf('alt="Game preview QR"') < readyPage.indexOf('alt="Progress QR"'));
|
|
484
499
|
assert.match(readyPage, /data-phase="preview"/);
|
|
485
500
|
assert.equal(output.filter(e => e.terminal).length, 1);
|
|
486
501
|
assert.equal(webPolls, 7, 'Keep polling the same task until preview URL is available');
|
package/package.json
CHANGED
package/vibe-upload.md
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
# Vibe
|
|
2
|
-
|
|
1
|
+
# Vibe Upload and Preview
|
|
3
2
|
|
|
4
3
|
`ttmg upload --vibe` 将本地小游戏上传到 TikTok Mini Games。用户提供游戏标题和头像,在手机 TikTok 扫码授权后,CLI 自动完成头像、应用资料和代码包上传。Skill 负责准备工程、调用命令和读取结果;CLI 负责素材校验、打包、授权轮询及上传。
|
|
5
4
|
|
|
6
|
-
`0.4.6-vibe-beta.
|
|
5
|
+
`0.4.6-vibe-beta.19` 支持手机授权、自动上传、同任务轮询和双二维码预览。预览就绪后优先展示「扫码预览」,保留原码用于「查看进度」。页面按浏览器语言显示中英文,两个二维码的图案尺寸和白边一致。当前为 PPE 测试链路,上传完成不代表审核通过或正式发布。
|
|
6
|
+
|
|
7
|
+
Version `0.4.6-vibe-beta.19` supports phone authorization, automatic upload, same-task polling and two QR codes: Preview first, then Check progress. Page language follows the browser, and both QR codes use equal visible dimensions. This is a PPE trial; upload success is not review approval or production release.
|
|
7
8
|
|
|
8
9
|
## Quick Start
|
|
9
10
|
|
|
@@ -23,18 +24,24 @@ ttmg upload --vibe --dir ./game --title "星环守卫" --icon ./avatar.png --for
|
|
|
23
24
|
|
|
24
25
|
3. CLI 自动上传头像、应用资料和代码包,页面展示进度与结果。
|
|
25
26
|
|
|
26
|
-
4.
|
|
27
|
+
4. 上传成功后短暂显示成功提示,随后展示预览加载态并保留原二维码;CLI 持续查询同一任务,兼容状态 9(等待提交代码审核)。拿到有效 `previewUrl` 后,预览码优先显示,原码保留为「查看进度」,无需重复上传。
|
|
27
28
|
|
|
28
|
-
5. Skill 收到 `terminal=true`、`status=uploaded`、`uploaded=true`、`previewReady=true`、`simulation=false` 且退出码为 0
|
|
29
|
+
5. Skill 收到 `terminal=true`、`status=uploaded`、`uploaded=true`、`previewReady=true`、`simulation=false` 且退出码为 0 后,提示用户扫码预览。主码的 `qrPurpose` 为 `game_preview` 或 `tool_preview`。
|
|
29
30
|
|
|
30
31
|
已有 ZIP 时,用 `--package ./game.zip` 替换 `--dir ./game`。无需预先执行 `ttmg login` 或配置 PPE 环境。描述和分类可省略,CLI 使用默认资料;完整参数见下文。
|
|
31
32
|
|
|
32
33
|
## 安装与本地预检
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
### Tool PPE 试用
|
|
36
|
+
|
|
37
|
+
使用 `--minis-type MINI_TOOL`,省略时仍上传小游戏。首轮使用 Native 工程或 ZIP,固定 `EFFICIENCY_AND_OFFICE` 分类、`region=US` 和 `min_sdk_version=1.0.0`,不传游戏子类型。暂不接受分类和旧 `--app-info` 覆盖;描述仍可用 `--description` 设置。此试用尚未代表真机预览或审核通过,也不包含 H5 包支持。
|
|
38
|
+
|
|
39
|
+
在独立 Tool 工程目录调用:`ttmg upload --vibe --minis-type MINI_TOOL --dir ./tool --title "Focus Timer" --icon ./icon.png --format ndjson`。工程身份首次创建后固定类型,不要复用小游戏的 `.ttmg/vibe-project.json`。
|
|
40
|
+
|
|
41
|
+
安装后确认版本为 `0.4.6-vibe-beta.19`。能力声明中 `authorizationMode=qr`、`previewQr=true`,预览主码用途为 `game_preview`(Tool 为 `tool_preview`),原码仍保留。支持 Node.js 20.19.1–20.x 或 22 及以上版本。
|
|
35
42
|
|
|
36
43
|
```sh
|
|
37
|
-
npm install -g @ttmg/cli@0.4.6-vibe-beta.
|
|
44
|
+
npm install -g @ttmg/cli@0.4.6-vibe-beta.19
|
|
38
45
|
ttmg --version
|
|
39
46
|
ttmg capabilities --format json
|
|
40
47
|
```
|
|
@@ -119,7 +126,7 @@ ttmg upload --vibe --dir ./game --title "星环守卫" --icon ./avatar.png --cat
|
|
|
119
126
|
3. 收到 `uploading`,按 `uploadStep` 展示 `avatar`、`app_info`、`package` 进度。授权轮询和上传由 CLI 完成。
|
|
120
127
|
|
|
121
128
|
4. 收到 `waiting_preview` 时,素材已上传,保持原进程运行。CLI 使用同一任务继续查询;状态已完成但尚无预览链接时也继续等待。
|
|
122
|
-
5. 终态仍为 `status=uploaded`。`previewReady=true`
|
|
129
|
+
5. 终态仍为 `status=uploaded`。`previewReady=true` 表示后端预览链接已返回,主码使用该地址,`qrPurpose` 为 `game_preview` 或 `tool_preview`。等待失败时保留 `uploaded=true`,同时返回错误和 `nextAction=check_task`,不要重新上传。
|
|
123
130
|
|
|
124
131
|
## 扫码授权与自动上传
|
|
125
132
|
|
|
@@ -154,7 +161,7 @@ ttmg auth open --page-url <pageUrl> --format json
|
|
|
154
161
|
- `uploading`:根据 uploadStep 显示 avatar、app_info、package 的进度,不再询问授权。
|
|
155
162
|
|
|
156
163
|
- `waiting_preview`:uploaded=true、terminal=false,正在查询后端处理结果,等待预览链接。
|
|
157
|
-
- `uploaded`:terminal=true、uploaded=true、simulation=false 且退出码 0
|
|
164
|
+
- `uploaded`:terminal=true、uploaded=true、simulation=false 且退出码 0 表示上传完成;previewReady=true 时页面展示预览码并保留进度码,不代表审核通过或正式发布。
|
|
158
165
|
|
|
159
166
|
- `failed / cancelled`:根据 errorCode、message 和 nextAction 提示处理,不自动重传。
|
|
160
167
|
|
|
@@ -163,7 +170,7 @@ ttmg auth open --page-url <pageUrl> --format json
|
|
|
163
170
|
|
|
164
171
|
从真实创建任务请求发起前开始 `platformWrite=true`,表示已尝试平台写入,不代表创建或上传成功。只有内部离线测试、本机 HTTP 和 dry-run 保持 `platformWrite=false`。
|
|
165
172
|
|
|
166
|
-
按阶段补充:`taskId`(字符串,禁止转成 JS Number)、`pollIntervalSeconds`、`readyForInput`、`taskStatusRaw`(仅诊断)、`uploadStep`、`loadedBytes`、`totalBytes`、`pageUrl`、`qrPurpose
|
|
173
|
+
按阶段补充:`taskId`(字符串,禁止转成 JS Number)、`pollIntervalSeconds`、`readyForInput`、`taskStatusRaw`(仅诊断)、`uploadStep`、`loadedBytes`、`totalBytes`、`pageUrl`、`qrPurpose`(授权/等待时为 authorization_binding,预览时为 game_preview 或 tool_preview)、素材大小和包 SHA256。代码包字节进度达到 100% 不代表成功,必须等待最终上传回执。
|
|
167
174
|
|
|
168
175
|
`nextAction` 取值:`wait`、`scan_qr`、`fix_input`、`restart_authorization`、`check_task`、`none`。已开始上传后发生失败/超时,先检查 task,不能直接再次创建并重传。预览等待超时返回 `VIBE_PREVIEW_TIMEOUT`;查询失败、取消或页面失败均保留 `uploaded=true`,并返回错误及 `nextAction=check_task`。
|
|
169
176
|
|
|
@@ -171,13 +178,17 @@ ttmg auth open --page-url <pageUrl> --format json
|
|
|
171
178
|
|
|
172
179
|
退出码:成功/dry-run 为 0,失败为 1,Ctrl+C 为 130。进程或宿主意外终止导致没有终态时,不得认定上传成功。
|
|
173
180
|
|
|
174
|
-
轮询间隔使用创建任务返回的 `poll_interval_seconds
|
|
181
|
+
轮询间隔使用创建任务返回的 `poll_interval_seconds`。本版本将 `--timeout`(包括授权、上传和等待预览的整体超时)延长为默认 1800 秒,最大 3600 秒。不自动重试写接口、不实现跨进程续传。
|
|
182
|
+
|
|
183
|
+
`--timeout` covers authorization, upload and preview polling. It defaults to 1800 seconds and supports up to 3600 seconds. Write requests are not automatically retried; cross-process resume is not supported.
|
|
175
184
|
|
|
176
185
|
## 页面与素材
|
|
177
186
|
|
|
178
|
-
|
|
187
|
+
页面展示本次标题、头像、描述及分类,按浏览器语言显示中英文。后台更新变化内容,不整页刷新;上传期间保留原二维码,上传成功后短暂提示,再显示预览加载态。返回有效 preview_url 后,以真实预览码替换占位,原码保留为「查看进度」;双码实际图案均为 160×160,外部白边一致。预览就绪或查询结束后停止等待动画。重新打开 pageUrl 直接显示当前状态,不会创建新任务或重新上传。
|
|
188
|
+
|
|
189
|
+
页面只监听 127.0.0.1,二维码才由手机扫描,手机不访问 pageUrl。本版本在任务进行中保持页面可用,CLI 结束或意外退出后再保留 30 分钟,内容驻留内存;测试可设置 `TTMG_VIBE_PAGE_TTL_MS`(1—1800000)。上传前主动取消会立即关闭页面;成功、上传后取消及可显示的失败页面保留至到期。页面保留时间不会延长后端二维码的有效期。不要分享本地页面地址。
|
|
179
190
|
|
|
180
|
-
|
|
191
|
+
the page listens only on 127.0.0.1 and stays available while the CLI is running, then for 30 minutes after it finishes or exits unexpectedly. Cancelling before upload closes the page immediately. The retention window does not extend backend QR validity.
|
|
181
192
|
|
|
182
193
|
CLI 执行本地项目检查和 ZIP 打包,并按 PNG/JPEG、1024×1024、5 MiB 校验上传头像。文件头检查不等于完整图像解码;ZIP 非空、大小和文件头检查不等于完整内容、解压总大小校验或平台审核。
|
|
183
194
|
|
|
@@ -210,7 +221,7 @@ TTMG_VIBE_BACKEND=local-http TTMG_VIBE_API=http://127.0.0.1:3100 ttmg upload --v
|
|
|
210
221
|
|
|
211
222
|
**v1 → v2 兼容性变化**:旧 UUID 换 Token 的三接口协议废弃,`--vibe-authorize-url` 返回明确错误;授权地址来自 landing_url。不再输出旧 preview_ready 状态、uploadId、reviewStatus。
|
|
212
223
|
|
|
213
|
-
beta.
|
|
224
|
+
beta.19 保留 waiting_preview 状态、uploaded 终态和 schemaVersion=2,兼容状态 9。与 beta.18 相比,previewQr=true,主码使用 previewUrl,qrPurpose 为 game_preview 或 tool_preview,原码以「查看进度」保留。Skill 应按 qrPurpose 判断用途。普通不带 --vibe 的 upload 不变。
|
|
214
225
|
|
|
215
226
|
2026-09-14 已核对 BFF 源码:头像 PNG/JPEG、1024×1024、5 MiB;Native ZIP 和解压总大小上限 100 MiB。本地暂未实现完整 ZIP 内容及解压总大小校验。2026-09-16 本地构建使用自有测试素材跑通真实 PPE 手机扫码授权、头像、默认资料和代码包上传。该验证不覆盖全部游戏或正式环境。后端接口调整留在适配层处理;若用户动作或成功语义改变,应协商升级 CLI schema。
|
|
216
227
|
|