@ttmg/cli 0.4.6-vibe-beta.6 → 0.4.6-vibe-beta.7
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 +15 -3
- package/dist/index.js +193 -40
- package/dist/index.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/scripts/vibe-ppe-test-preload.cjs +23 -0
- package/dist/scripts/vibe-upload-demo.js +1 -1
- package/dist/scripts/vibe-upload-smoke.js +87 -4
- package/package.json +1 -1
- package/vibe-upload.md +29 -21
package/dist/package.json
CHANGED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Explicit test-process preload only; never loaded by the CLI. Replace HTTPS
|
|
2
|
+
// transport with a local fixture so --mock regressions cannot create real tasks.
|
|
3
|
+
const assert = require('node:assert/strict');
|
|
4
|
+
const http = require('node:http');
|
|
5
|
+
const https = require('node:https');
|
|
6
|
+
const port = Number(process.env.TTMG_VIBE_TEST_PORT);
|
|
7
|
+
assert.ok(Number.isInteger(port) && port > 0 && port < 65536);
|
|
8
|
+
https.request = (options, callback) => {
|
|
9
|
+
assert.equal(typeof options, 'object');
|
|
10
|
+
assert.equal(options.hostname, 'developers.tiktok.com');
|
|
11
|
+
assert.equal(options.protocol, 'https:');
|
|
12
|
+
assert.match(options.path, /^\/bff\/app_info\/minis_import\/(create_minis_import_task_for_agent|get_minis_import_task_status_for_agent)\//);
|
|
13
|
+
return http.request({
|
|
14
|
+
...options,
|
|
15
|
+
protocol: 'http:', hostname: '127.0.0.1', host: undefined, port,
|
|
16
|
+
agent: undefined, servername: undefined,
|
|
17
|
+
}, callback);
|
|
18
|
+
};
|
|
19
|
+
https.get = (options, callback) => {
|
|
20
|
+
const request = https.request(options, callback);
|
|
21
|
+
request.end();
|
|
22
|
+
return request;
|
|
23
|
+
};
|
|
@@ -29,7 +29,7 @@ fs.writeFileSync(
|
|
|
29
29
|
),
|
|
30
30
|
);
|
|
31
31
|
console.error(
|
|
32
|
-
'
|
|
32
|
+
'Integration demo: creates a real PPE task; authorization and uploads are simulated. No environment setup or phone authorization required.',
|
|
33
33
|
);
|
|
34
34
|
const child = spawn(
|
|
35
35
|
process.execPath,
|
|
@@ -4,6 +4,7 @@ const os = require('node:os');
|
|
|
4
4
|
const path = require('node:path');
|
|
5
5
|
const { spawn } = require('node:child_process');
|
|
6
6
|
const archiver = require('archiver');
|
|
7
|
+
const http = require('node:http');
|
|
7
8
|
|
|
8
9
|
const entry = path.resolve(__dirname, '../dist/index.js');
|
|
9
10
|
const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'ttmg-vibe-process-'));
|
|
@@ -23,6 +24,9 @@ const input = [
|
|
|
23
24
|
const png =
|
|
24
25
|
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+aXyoAAAAASUVORK5CYII=';
|
|
25
26
|
const children = new Set();
|
|
27
|
+
let fixtureServer;
|
|
28
|
+
const requests = [];
|
|
29
|
+
let failStatus = false;
|
|
26
30
|
const env = {
|
|
27
31
|
...process.env,
|
|
28
32
|
HOME: temp,
|
|
@@ -31,6 +35,7 @@ const env = {
|
|
|
31
35
|
TTMG_VIBE_PAGE_TTL_MS: '7000',
|
|
32
36
|
TTMG_VIBE_BACKEND: '',
|
|
33
37
|
TTMG_VIBE_API: '',
|
|
38
|
+
TTMG_VIBE_PPE: '',
|
|
34
39
|
};
|
|
35
40
|
|
|
36
41
|
function run(args, onEvent, extraEnv = {}) {
|
|
@@ -94,6 +99,31 @@ function events(result) {
|
|
|
94
99
|
.map(line => JSON.parse(line));
|
|
95
100
|
}
|
|
96
101
|
async function main() {
|
|
102
|
+
fixtureServer = http.createServer((req, res) => {
|
|
103
|
+
const url = new URL(req.url, 'http://127.0.0.1');
|
|
104
|
+
requests.push({ path: url.pathname, method: req.method, headers: req.headers });
|
|
105
|
+
req.resume();
|
|
106
|
+
const create = url.pathname.endsWith('/create_minis_import_task_for_agent/');
|
|
107
|
+
const status = url.pathname.endsWith('/get_minis_import_task_status_for_agent/');
|
|
108
|
+
assert.ok(create || status, 'Unexpected real upload endpoint');
|
|
109
|
+
assert.equal(req.headers['x-use-ppe'], '1');
|
|
110
|
+
assert.equal(req.headers['x-tt-env'], 'ppe_op_vc');
|
|
111
|
+
assert.equal(req.headers.cookie, undefined);
|
|
112
|
+
if (status) {
|
|
113
|
+
assert.equal(url.searchParams.get('task_id'), '7684370037737553941');
|
|
114
|
+
assert.equal(url.searchParams.get('import_token'), 'process-test-secret');
|
|
115
|
+
}
|
|
116
|
+
const data = create ? {
|
|
117
|
+
task_id: '7684370037737553941', import_token: 'process-test-secret',
|
|
118
|
+
landing_url: 'https://developers.tiktok.com/portal_h5/minis_import?import_token=process-test-secret',
|
|
119
|
+
poll_interval_seconds: 0.01,
|
|
120
|
+
} : { status: 1, ready_for_input: false };
|
|
121
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
122
|
+
res.end(JSON.stringify({ ...data, error_info: { code: failStatus && status ? 420500 : 0 } }));
|
|
123
|
+
});
|
|
124
|
+
await new Promise(resolve => fixtureServer.listen(0, '127.0.0.1', resolve));
|
|
125
|
+
env.TTMG_VIBE_TEST_PORT = String(fixtureServer.address().port);
|
|
126
|
+
env.NODE_OPTIONS = `--require ${JSON.stringify(path.join(__dirname, 'vibe-ppe-test-preload.cjs'))}`;
|
|
97
127
|
fs.writeFileSync(icon, Buffer.from(png, 'base64'));
|
|
98
128
|
await new Promise((resolve, reject) => {
|
|
99
129
|
const out = fs.createWriteStream(zip);
|
|
@@ -117,9 +147,9 @@ async function main() {
|
|
|
117
147
|
assert.equal(success.code, 0, success.stderr);
|
|
118
148
|
assert.match(bindingHtml, /Upload to TikTok/);
|
|
119
149
|
assert.match(bindingHtml, /class="game-avatar"/);
|
|
120
|
-
assert.match(bindingHtml, /Mock
|
|
150
|
+
assert.match(bindingHtml, /PPE \+ Mock/);
|
|
121
151
|
assert.match(bindingHtml, /data-phase="binding"/);
|
|
122
|
-
assert.match(bindingHtml,
|
|
152
|
+
assert.match(bindingHtml, /Phone authorization is not required/);
|
|
123
153
|
assert.doesNotMatch(bindingHtml, /<form|<input/);
|
|
124
154
|
const sequence = events(success);
|
|
125
155
|
const final = sequence[sequence.length - 1];
|
|
@@ -129,7 +159,16 @@ async function main() {
|
|
|
129
159
|
assert.equal(final.nextAction, 'none');
|
|
130
160
|
assert.equal(sequence.filter(e => e.terminal).length, 1);
|
|
131
161
|
assert.equal(final.uploaded, true);
|
|
132
|
-
assert.equal(final.platformWrite,
|
|
162
|
+
assert.equal(final.platformWrite, true);
|
|
163
|
+
assert.equal(final.backend, 'ppe-mock');
|
|
164
|
+
assert.equal(final.taskStatusRaw, 1);
|
|
165
|
+
assert.equal(final.readyForInput, false);
|
|
166
|
+
assert.equal(final.simulatedReadyForInput, true);
|
|
167
|
+
assert.equal(final.simulatedTaskStatusRaw, 3);
|
|
168
|
+
assert.equal(requests.length, 4);
|
|
169
|
+
assert.equal(requests[0].method, 'POST');
|
|
170
|
+
assert.ok(requests.slice(1).every(r => r.method === 'GET'));
|
|
171
|
+
assert.doesNotMatch(success.stdout, /process-test-secret/);
|
|
133
172
|
assert.equal(final.simulation, true);
|
|
134
173
|
const progress = sequence.filter(
|
|
135
174
|
e => e.status === 'uploading' && e.loadedBytes !== undefined,
|
|
@@ -145,7 +184,7 @@ async function main() {
|
|
|
145
184
|
1,
|
|
146
185
|
);
|
|
147
186
|
const previewHtml = await (await fetch(final.pageUrl)).text();
|
|
148
|
-
assert.match(previewHtml, /
|
|
187
|
+
assert.match(previewHtml, /Simulated upload complete/);
|
|
149
188
|
assert.match(previewHtml, /data-phase="complete"/);
|
|
150
189
|
assert.doesNotMatch(previewHtml, /QR|preview|review|Publish to TikTok/);
|
|
151
190
|
|
|
@@ -160,6 +199,44 @@ async function main() {
|
|
|
160
199
|
assert.equal(JSON.parse(json.stdout).simulation, true);
|
|
161
200
|
assert.match(json.stderr, /http:\/\/127\.0\.0\.1:/);
|
|
162
201
|
|
|
202
|
+
const missingPpe = await run([...input, '--format', 'json'], undefined, {
|
|
203
|
+
TTMG_VIBE_BACKEND: 'ppe',
|
|
204
|
+
});
|
|
205
|
+
assert.equal(missingPpe.code, 1);
|
|
206
|
+
assert.equal(events(missingPpe)[0].errorCode, 'VIBE_INVALID_PPE_ENV');
|
|
207
|
+
assert.equal(events(missingPpe)[0].platformWrite, false);
|
|
208
|
+
const dryPpe = await run([...input, '--dry-run', '--format', 'json'], undefined, {
|
|
209
|
+
TTMG_VIBE_BACKEND: 'ppe', TTMG_VIBE_PPE: 'ppe_op_vc',
|
|
210
|
+
});
|
|
211
|
+
assert.equal(dryPpe.code, 0, dryPpe.stderr);
|
|
212
|
+
assert.equal(events(dryPpe)[0].status, 'prepared');
|
|
213
|
+
assert.equal(events(dryPpe)[0].platformWrite, false);
|
|
214
|
+
const missingHybrid = await run([...input, '--format', 'json'], undefined, {
|
|
215
|
+
TTMG_VIBE_BACKEND: 'ppe-mock',
|
|
216
|
+
});
|
|
217
|
+
assert.equal(events(missingHybrid)[0].errorCode, 'VIBE_INVALID_PPE_ENV');
|
|
218
|
+
assert.equal(events(missingHybrid)[0].platformWrite, false);
|
|
219
|
+
const dryHybrid = await run([...input, '--dry-run', '--format', 'json'], undefined, {
|
|
220
|
+
TTMG_VIBE_BACKEND: 'ppe-mock', TTMG_VIBE_PPE: 'ppe_op_vc',
|
|
221
|
+
});
|
|
222
|
+
assert.equal(dryHybrid.code, 0, dryHybrid.stderr);
|
|
223
|
+
assert.equal(events(dryHybrid)[0].status, 'prepared');
|
|
224
|
+
assert.equal(events(dryHybrid)[0].platformWrite, false);
|
|
225
|
+
const hybridMockOverride = await run([...input, '--mock', '--poll-interval', '0.01', '--format', 'json'], undefined, {
|
|
226
|
+
TTMG_VIBE_BACKEND: 'ppe-mock', TTMG_VIBE_PPE: 'ppe_op_vc',
|
|
227
|
+
});
|
|
228
|
+
assert.equal(hybridMockOverride.code, 0, hybridMockOverride.stderr);
|
|
229
|
+
assert.equal(events(hybridMockOverride)[0].backend, 'ppe-mock');
|
|
230
|
+
assert.equal(events(hybridMockOverride)[0].platformWrite, true);
|
|
231
|
+
|
|
232
|
+
failStatus = true;
|
|
233
|
+
const realFailure = await run([...input, '--mock', '--format', 'json']);
|
|
234
|
+
assert.equal(realFailure.code, 1, realFailure.stderr);
|
|
235
|
+
assert.equal(events(realFailure)[0].errorCode, 'VIBE_BACKEND_REJECTED');
|
|
236
|
+
assert.equal(events(realFailure)[0].uploadAttempted, false);
|
|
237
|
+
assert.equal(events(realFailure)[0].uploaded, false);
|
|
238
|
+
failStatus = false;
|
|
239
|
+
|
|
163
240
|
for (const [scenario, errorCode, attempted] of [
|
|
164
241
|
['denied', 'VIBE_AUTH_DENIED', false],
|
|
165
242
|
['upload-failed', 'VIBE_BACKEND_REQUEST_FAILED', true],
|
|
@@ -193,6 +270,11 @@ async function main() {
|
|
|
193
270
|
assert.equal(prepared.backend, 'none');
|
|
194
271
|
assert.equal(prepared.pageUrl, undefined);
|
|
195
272
|
assert.equal(prepared.uploadAttempted, false);
|
|
273
|
+
const beforeDryMock = requests.length;
|
|
274
|
+
const dryMock = await run([...input, '--mock', '--dry-run', '--format', 'json']);
|
|
275
|
+
assert.equal(dryMock.code, 0);
|
|
276
|
+
assert.equal(events(dryMock)[0].platformWrite, false);
|
|
277
|
+
assert.equal(requests.length, beforeDryMock);
|
|
196
278
|
|
|
197
279
|
const project = path.join(temp, 'project');
|
|
198
280
|
fs.mkdirSync(project);
|
|
@@ -254,5 +336,6 @@ main()
|
|
|
254
336
|
})
|
|
255
337
|
.finally(() => {
|
|
256
338
|
for (const child of children) child.kill('SIGKILL');
|
|
339
|
+
fixtureServer?.close();
|
|
257
340
|
fs.rmSync(temp, { recursive: true, force: true });
|
|
258
341
|
});
|
package/package.json
CHANGED
package/vibe-upload.md
CHANGED
|
@@ -2,25 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
## 中文
|
|
4
4
|
|
|
5
|
-
本次范围:本地准备 → 扫码绑定 → 上传头像 → 提交名称和头像信息 → 上传代码包 →
|
|
5
|
+
本次范围:本地准备 → 扫码绑定 → 上传头像 → 提交名称和头像信息 → 上传代码包 → 上传完成。不包含预览、审核或正式发布。当前创建/查询接 PPE,授权和素材上传仍为模拟。
|
|
6
6
|
|
|
7
7
|
### 博华如何接入
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
开发环境先配置一次(属于运行配置,不写成 Skill 的流程分支):
|
|
9
|
+
继续使用 `--mock`,无需本地环境配置。CLI 内置 `ppe_op_vc` 创建任务,展示授权二维码,并查询三次真实状态;之后模拟授权和头像/信息/代码包上传。二维码可用于查看端内授权页,不要求完成授权。真实请求失败仍报错,素材不会上传到 PPE。上线时由 CLI 版本去掉 PPE 路由,Skill 不负责切换环境。
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
export TTMG_VIBE_BACKEND=mock
|
|
15
|
-
```
|
|
11
|
+
Skill 只引导开发者准备游戏名称、本地头像和工程,然后调用 CLI、读取结构化状态。不调用平台接口、不保存 Token、不自行轮询、不增加名称头像表单。
|
|
16
12
|
|
|
17
13
|
Skill 固定调用:
|
|
18
14
|
|
|
19
15
|
```sh
|
|
20
|
-
ttmg upload --vibe --dir ./game --title "星环守卫" --icon ./avatar.png --format ndjson
|
|
16
|
+
ttmg upload --vibe --mock --dir ./game --title "星环守卫" --icon ./avatar.png --format ndjson
|
|
21
17
|
```
|
|
22
18
|
|
|
23
|
-
未安装本地构建时,以 `node /绝对路径/cli/dist/index.js` 替换 `ttmg
|
|
19
|
+
未安装本地构建时,以 `node /绝对路径/cli/dist/index.js` 替换 `ttmg`。已调通的步骤接真实接口、未调通的步骤继续模拟,由 CLI 内部维护;Skill 不跟着 HTTP 字段和联调环境变化。
|
|
24
20
|
|
|
25
21
|
可先检查素材,不创建 task、不打开页面:
|
|
26
22
|
|
|
@@ -45,15 +41,19 @@ ttmg upload --vibe --dir ./game --title "星环守卫" --icon ./avatar.png --dry
|
|
|
45
41
|
|
|
46
42
|
固定字段:`schemaVersion`、`command`、`mode`、`operationId`、`ts`、`status`、`terminal`、`nextAction`、`message`、`backend`、`simulation`、`platformWrite`、`uploadAttempted`、`uploaded`、`errorCode`、`error`。
|
|
47
43
|
|
|
44
|
+
从真实创建任务请求发起前开始 `platformWrite=true`,表示已尝试平台写入,不代表创建或上传成功。只有内部离线测试、本机 HTTP 和 dry-run 保持 `platformWrite=false`。
|
|
45
|
+
|
|
46
|
+
`backend=ppe-mock` 时 `simulation=true`、`simulationStartsAt=authorization`;创建任务前开始 `platformWrite=true`。`taskStatusRaw`、`readyForInput` 始终保留最后一次真实查询值,模拟状态单独放在 `simulatedTaskStatusRaw`、`simulatedReadyForInput`。终态仍为 `uploaded`,但只表示模拟上传完成,不代表真实授权或上传成功;Skill 必须保留模拟提示。
|
|
47
|
+
|
|
48
48
|
按阶段补充:`taskId`(字符串,禁止转成 JS Number)、`pollIntervalSeconds`、`readyForInput`、`taskStatusRaw`(仅诊断)、`uploadStep`、`loadedBytes`、`totalBytes`、`pageUrl`、`qrPurpose=authorization_binding`、素材大小和包 SHA256。代码包字节进度达到 100% 不代表成功,必须等待最终上传回执。
|
|
49
49
|
|
|
50
50
|
`nextAction` 取值:`wait`、`scan_qr`、`fix_input`、`restart_authorization`、`check_task`、`none`。已开始上传后发生失败/超时,先检查 task,不能直接再次创建并重传。若上传已完成但页面失败,仍保留 `uploaded=true`,同时返回错误;Skill 提示上传已完成但展示失败。
|
|
51
51
|
|
|
52
|
-
退出码:成功/dry-run 为 0,失败为 1,Ctrl+C 为 130
|
|
52
|
+
退出码:成功/dry-run 为 0,失败为 1,Ctrl+C 为 130。进程或宿主意外终止导致没有终态时,不得认定上传成功。模拟必须显示为模拟:即使 `uploaded=true`,`simulation=true` 时也只表示模拟步骤完成。纯 Mock 的 `platformWrite=false`;混合联调创建过真实任务时为 true,但都不代表真实上传。
|
|
53
53
|
|
|
54
54
|
### Mock 覆盖与演示
|
|
55
55
|
|
|
56
|
-
`npm run demo:vibe`
|
|
56
|
+
`npm run demo:vibe` 会构建并用临时工程和头像运行 CLI;真实检查、ZIP 打包和 PPE 创建/查询,模拟授权与上传。需要后端可访问,无需完成手机授权。结果页在进程结束后继续保留至到期。
|
|
57
57
|
|
|
58
58
|
```sh
|
|
59
59
|
ttmg upload --vibe --package ./game.zip --title "测试游戏" --icon ./avatar.png --mock --format ndjson
|
|
@@ -73,7 +73,7 @@ ttmg upload --vibe --package ./game.zip --title "测试游戏" --icon ./avatar.p
|
|
|
73
73
|
|
|
74
74
|
Mock 的 denied 是交互测试场景,真实端内拒绝授权如何映射错误仍待后端确认。Mock 不请求任何远端,不更改平台登录态,不创建真实应用。
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
轮询间隔使用创建任务返回的 `poll_interval_seconds`,第三次真实查询后模拟授权。`--poll-interval` 仅用于覆盖测试间隔。`--timeout` 是整体超时,默认 300 秒。不自动重试写接口、不实现跨进程续传。
|
|
77
77
|
|
|
78
78
|
### 页面与素材
|
|
79
79
|
|
|
@@ -105,7 +105,7 @@ Mock 与本地 HTTP 共用请求构造、业务响应校验及状态映射,基
|
|
|
105
105
|
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
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
维护者可使用 `--vibe-api http://127.0.0.1:3100` 验证本机 HTTP 合同,只接受回环 IP,不跟随重定向、不读取 Cookie;本地服务需实现上表协议。博华试用无需此选项,直接 `--mock`,PPE 路由由 CLI 内置。正式环境未开放。
|
|
109
109
|
|
|
110
110
|
**v1 → v2 兼容性变化**:旧 UUID 换 Token 的三接口协议废弃,`--vibe-authorize-url` 返回明确错误;授权地址来自 landing_url。不再输出 preview_ready、game_preview、uploadId、reviewStatus。Skill 必须校验 schemaVersion=2,并按新终态处理。普通不带 --vibe 的 upload 不变。
|
|
111
111
|
|
|
@@ -121,17 +121,17 @@ TTMG_VIBE_BACKEND=local-http TTMG_VIBE_API=http://127.0.0.1:3100 ttmg upload --v
|
|
|
121
121
|
|
|
122
122
|
### Skill entry
|
|
123
123
|
|
|
124
|
-
Scope: local preparation → authorization binding → avatar upload → app-info submission → package upload → **upload complete**. No preview, review or production release.
|
|
124
|
+
Scope: local preparation → authorization binding → avatar upload → app-info submission → package upload → **upload complete**. No preview, review or production release. Mock and loopback HTTP cover this contract. PPE additionally supports real task creation, polling and the authorization QR only; material upload and production access are not enabled.
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
Keep the existing --mock command; no environment setup is required:
|
|
127
127
|
|
|
128
128
|
```sh
|
|
129
|
-
ttmg upload --vibe --dir ./game --title "My game" --icon ./avatar.png --format ndjson
|
|
129
|
+
ttmg upload --vibe --mock --dir ./game --title "My game" --icon ./avatar.png --format ndjson
|
|
130
130
|
```
|
|
131
131
|
|
|
132
132
|
Use `--package ./game.zip` instead of --dir for a prepared ZIP. Use `--dry-run --format json` for local preparation only. When running a source build, replace ttmg with `node /absolute/path/cli/dist/index.js`. Explicit --mock or --vibe-api overrides environment selection; an unconfigured backend fails instead of silently simulating.
|
|
133
133
|
|
|
134
|
-
The Skill gathers the title and user-selected local icon in conversation. It does not call backend APIs, store credentials, poll services, or introduce a metadata form.
|
|
134
|
+
The Skill gathers the title and user-selected local icon in conversation. It does not call backend APIs, store credentials, poll services, or introduce a metadata form. The CLI internally connects working steps to PPE and simulates the remaining steps. Removing PPE at production launch is a CLI release change, not a Skill configuration step.
|
|
135
135
|
|
|
136
136
|
### Output and decisions
|
|
137
137
|
|
|
@@ -148,17 +148,21 @@ Schema **2** provides NDJSON snapshots under one operationId, with exactly one f
|
|
|
148
148
|
|
|
149
149
|
Stable fields: schemaVersion, command, mode, operationId, ts, status, terminal, nextAction, message, backend, simulation, platformWrite, uploadAttempted, uploaded, errorCode, error. Optional evidence: taskId (string), taskStatusRaw (diagnostics only), pollIntervalSeconds, readyForInput, uploadStep, loadedBytes, totalBytes, pageUrl, qrPurpose=authorization_binding, sizes and packageSha256.
|
|
150
150
|
|
|
151
|
+
platformWrite becomes true immediately before attempting real task creation, not as proof of successful creation or upload. Only injected offline tests, loopback HTTP and dry-run keep platformWrite=false.
|
|
152
|
+
|
|
153
|
+
For backend=ppe-mock, simulation=true and simulationStartsAt=authorization. platformWrite becomes true before real task creation. taskStatusRaw and readyForInput retain the last real observation; simulatedTaskStatusRaw and simulatedReadyForInput describe the local demo separately. The uploaded terminal state means simulated completion only; the Skill must retain the simulation label.
|
|
154
|
+
|
|
151
155
|
nextAction is wait, scan_qr, fix_input, restart_authorization, check_task or none. If upload was attempted, verify the task before retrying. If a result page fails after confirmed upload, uploaded remains true alongside the error. Progress at 100% is not a receipt. Missing terminal output after a killed process is not success. Exit codes: 0 success/dry-run, 1 failure, 130 cancellation.
|
|
152
156
|
|
|
153
|
-
Mock
|
|
157
|
+
Pure Mock reports simulation=true and platformWrite=false. Hybrid PPE + Mock reports platformWrite=true once real task creation is attempted, but uploaded=true still means simulated completion only. No backend exposes credentials or raw HTTP errors. Backend integration must preserve this public contract; changes to user actions or completion semantics require an explicit schema migration.
|
|
154
158
|
|
|
155
159
|
### Mock, UI and inputs
|
|
156
160
|
|
|
157
|
-
Run `npm run demo:vibe` for the
|
|
161
|
+
Run `npm run demo:vibe` for the CLI with temporary game/icon fixtures. Local checking, ZIP creation and PPE task creation/polling are real; authorization and uploads are simulated. Backend access is required, phone authorization is not. Scenarios: success, denied, expired, timeout, upload-failed, processing, icon-failed, info-failed. Use --mock-scenario and --timeout to test failures. Denial is a mock interaction scenario, not a confirmed backend error mapping.
|
|
158
162
|
|
|
159
163
|
The same local page changes from binding QR to upload progress and completion, without preview QR or metadata form. Failures clear the binding QR. It listens only on 127.0.0.1, retains content in memory, and expires after 15 minutes; the phone scans the QR rather than visiting pageUrl. Success/failure pages remain after CLI completion; cancellation closes the page. Test TTL: TTMG_VIBE_PAGE_TTL_MS=1..900000.
|
|
160
164
|
|
|
161
|
-
The
|
|
165
|
+
The trial follows poll_interval_seconds and simulates input readiness after the third real query; --poll-interval is a testing override. The overall --timeout defaults to 300 seconds. No automatic write retries or cross-process resume.
|
|
162
166
|
|
|
163
167
|
The supplied title/icon/package bytes are fixed before authorization. Direct files are checked for size and signatures, not decoded or fully inspected. PNG/JPEG/WebP and 10 MiB icon / 100 MiB ZIP limits are provisional local guards, not confirmed platform rules. Project packaging excludes .ttmg, .env\*, PEM and KEY files, not every possible secret.
|
|
164
168
|
|
|
@@ -168,7 +172,11 @@ A non-secret source_project_id is persisted in .ttmg/vibe-project.json under the
|
|
|
168
172
|
|
|
169
173
|
The five endpoint contracts are listed in the table above. Mock and loopback HTTP share request construction and response validation. The HTTP adapter implements POST create → GET status with task_id/import_token query parameters → Multipart icon → JSON app_info → Multipart package with asset_type=2 (Native). Multipart Content-Type includes its generated boundary; JSON i64 task IDs are encoded losslessly.
|
|
170
174
|
|
|
171
|
-
A business-success response requires error_info.code=0. Package completion additionally requires package_uri and a valid non-failure status; no polling until SUCCEEDED. No platform Cookies or redirects are used. Configure TTMG_VIBE_BACKEND=local-http plus TTMG_VIBE_API=http://127.0.0.1:3100, or pass --vibe-api explicitly
|
|
175
|
+
A business-success response requires error_info.code=0. Package completion additionally requires package_uri and a valid non-failure status; no polling until SUCCEEDED. No platform Cookies or redirects are used. Configure TTMG_VIBE_BACKEND=local-http plus TTMG_VIBE_API=http://127.0.0.1:3100, or pass --vibe-api explicitly; this option rejects remote hosts.
|
|
176
|
+
|
|
177
|
+
For internal PPE integration, set TTMG_VIBE_BACKEND=ppe and TTMG_VIBE_PPE=ppe_op_vc without changing the Skill command. This uses real creation/polling on developers.tiktok.com and encodes the QR as aweme://webview?url=<encodeURIComponent(landing_url)>. The phone must use the same lane. OAuth is not bypassed or simulated. Complete upload inputs are not integrated yet: after authorization, VIBE_REAL_UPLOAD_NOT_READY stops the flow before any material upload. Production is not enabled.
|
|
178
|
+
|
|
179
|
+
The normal --mock trial internally uses ppe_op_vc without environment variables. Task creation, QR and three polls are real; authorization, icon, app-info and package uploads are simulated with separate in-memory credentials. This changes --mock from an offline run into a PPE integration trial: platformWrite becomes true when task creation is attempted, while simulation remains true. No real material-upload endpoint is called, and real request failures still fail. --mock-scenario controls only simulated steps. Offline unit/process tests use injected transports, not a separate developer configuration.
|
|
172
180
|
|
|
173
181
|
Schema-v1 UUID endpoints, --vibe-authorize-url, preview_ready, game_preview, uploadId and reviewStatus are retired. The deprecated option returns an explicit error. Ordinary upload without --vibe is unchanged.
|
|
174
182
|
|