@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/README.md
CHANGED
|
@@ -21,9 +21,21 @@ npm install @ttmg/cli --save-dev
|
|
|
21
21
|
|
|
22
22
|
#### Login 登录
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
For the beta.7 trial, run `ttmg upload --vibe --mock` without environment setup.
|
|
25
|
+
Task creation/polling use the built-in PPE lane; authorization and uploads remain
|
|
26
|
+
simulated. Unlike beta.6, this is not offline: backend access is required and task
|
|
27
|
+
creation is a real platform write. See [Vibe binding/upload](vibe-upload.md).
|
|
28
|
+
beta.7 试用直接运行 `ttmg upload --vibe --mock`,不用登录或配置环境变量。
|
|
29
|
+
创建/查询任务走 CLI 内置 PPE,授权/上传继续模拟;与 beta.6 不同,不再是全程离线。
|
|
30
|
+
需要后端可访问,会创建真实任务,不上传游戏、不预览、不审核或发布。
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
npm install -g @ttmg/cli@0.4.6-vibe-beta.7
|
|
34
|
+
ttmg upload --vibe --mock --dir ./game --title "My game" --icon ./avatar.png --format ndjson
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
PPE routing will be removed in a production release without Skill environment setup.
|
|
38
|
+
上线时由 CLI 去掉 PPE 路由,不让 Skill 配置或切换环境。
|
|
27
39
|
|
|
28
40
|
For the existing authenticated platform workflow, log in first; this does not apply to the local Vibe prototype above.
|
|
29
41
|
原有需要平台鉴权的流程仍先登录;上述 Vibe 本地原型不需要此步骤。
|
package/dist/index.js
CHANGED
|
@@ -7277,12 +7277,12 @@ const messages = {
|
|
|
7277
7277
|
'cli.command.upload.servePreviewPage': 'Serve a temporary local preview page with test-account and preview QR codes',
|
|
7278
7278
|
'cli.command.upload.noOpen': 'Do not open the local preview page in the default browser',
|
|
7279
7279
|
'cli.command.upload.format': 'Output format: text | json | ndjson',
|
|
7280
|
-
'cli.command.upload.vibe': 'Skill-guided binding and upload (schema v2; mock
|
|
7280
|
+
'cli.command.upload.vibe': 'Skill-guided binding and upload (schema v2; use --mock for the integration trial)',
|
|
7281
7281
|
'cli.command.upload.vibeTitle': 'Game title supplied by the Skill (--vibe)',
|
|
7282
7282
|
'cli.command.upload.vibeIcon': 'Local PNG/JPEG/WebP selected by the user (--vibe)',
|
|
7283
7283
|
'cli.command.upload.vibePackage': 'Prepared ZIP instead of --dir (--vibe)',
|
|
7284
7284
|
'cli.command.upload.vibeDryRun': 'Validate and package locally without authorization or upload (--vibe)',
|
|
7285
|
-
'cli.command.upload.vibeMock': '
|
|
7285
|
+
'cli.command.upload.vibeMock': 'Integration trial: real PPE task creation/polling, simulated authorization/upload; no environment setup or phone authorization (--vibe)',
|
|
7286
7286
|
'cli.command.upload.vibeScenario': 'Mock: success | denied | expired | timeout | upload-failed | processing | icon-failed | info-failed',
|
|
7287
7287
|
'cli.command.upload.vibeApi': 'Minis Import API origin on a loopback IP only (--vibe)',
|
|
7288
7288
|
'cli.command.upload.vibeAuthorizeUrl': 'Obsolete: remove this option; task creation returns landing_url (--vibe)',
|
|
@@ -7537,12 +7537,12 @@ const messages = {
|
|
|
7537
7537
|
'cli.command.upload.servePreviewPage': '启动包含测试账号与预览二维码的临时本地页面',
|
|
7538
7538
|
'cli.command.upload.noOpen': '不使用默认浏览器打开本地预览页面',
|
|
7539
7539
|
'cli.command.upload.format': '输出格式:text | json | ndjson',
|
|
7540
|
-
'cli.command.upload.vibe': 'Skill 引导绑定上传(schema v2
|
|
7540
|
+
'cli.command.upload.vibe': 'Skill 引导绑定上传(schema v2;使用 --mock 联调试用)',
|
|
7541
7541
|
'cli.command.upload.vibeTitle': 'Skill 提供的游戏标题(--vibe)',
|
|
7542
7542
|
'cli.command.upload.vibeIcon': '用户指定的本地 PNG/JPEG/WebP 图片(--vibe)',
|
|
7543
7543
|
'cli.command.upload.vibePackage': '已准备的 ZIP,与 --dir 二选一(--vibe)',
|
|
7544
7544
|
'cli.command.upload.vibeDryRun': '仅本地校验与打包,不授权、不上传(--vibe)',
|
|
7545
|
-
'cli.command.upload.vibeMock': '
|
|
7545
|
+
'cli.command.upload.vibeMock': '联调试用:真实 PPE 创建/查询,模拟授权/上传;无需环境配置或完成手机授权(--vibe)',
|
|
7546
7546
|
'cli.command.upload.vibeScenario': '模拟:success | denied | expired | timeout | upload-failed | processing | icon-failed | info-failed',
|
|
7547
7547
|
'cli.command.upload.vibeApi': 'Minis Import 接口地址,仅接受回环 IP(--vibe)',
|
|
7548
7548
|
'cli.command.upload.vibeAuthorizeUrl': '已废弃,请移除此参数;授权地址由创建任务返回(--vibe)',
|
|
@@ -15993,7 +15993,7 @@ async function authStatus(options = {}) {
|
|
|
15993
15993
|
return result;
|
|
15994
15994
|
}
|
|
15995
15995
|
|
|
15996
|
-
var version = "0.4.6-vibe-beta.
|
|
15996
|
+
var version = "0.4.6-vibe-beta.7";
|
|
15997
15997
|
var engines = {
|
|
15998
15998
|
node: ">=20.19.1 <21 || >=22"
|
|
15999
15999
|
};
|
|
@@ -16017,11 +16017,28 @@ function getCapabilities() {
|
|
|
16017
16017
|
command: 'ttmg upload --vibe --mock --format ndjson',
|
|
16018
16018
|
option: '--vibe',
|
|
16019
16019
|
available: true,
|
|
16020
|
-
platformWrite:
|
|
16021
|
-
readiness: '
|
|
16020
|
+
platformWrite: true,
|
|
16021
|
+
readiness: 'ppe-authorization-integration',
|
|
16022
16022
|
schemaVersion: '2',
|
|
16023
16023
|
backendEnvironment: 'TTMG_VIBE_BACKEND',
|
|
16024
|
-
backendModes: ['mock', 'local-http'],
|
|
16024
|
+
backendModes: ['mock', 'local-http', 'ppe', 'ppe-mock'],
|
|
16025
|
+
ppeMock: {
|
|
16026
|
+
environment: 'TTMG_VIBE_PPE',
|
|
16027
|
+
realOperations: ['taskCreation', 'statusPolling'],
|
|
16028
|
+
mockOperations: ['authorization', 'avatar', 'app_info', 'package'],
|
|
16029
|
+
simulateAfterPolls: 3,
|
|
16030
|
+
realUploadEnabled: false,
|
|
16031
|
+
simulationStartsAt: 'authorization',
|
|
16032
|
+
},
|
|
16033
|
+
ppe: {
|
|
16034
|
+
environment: 'TTMG_VIBE_PPE',
|
|
16035
|
+
host: 'developers.tiktok.com',
|
|
16036
|
+
taskCreation: true,
|
|
16037
|
+
statusPolling: true,
|
|
16038
|
+
authorizationScheme: 'aweme://webview?url=<encoded landing_url>',
|
|
16039
|
+
uploadEnabled: false,
|
|
16040
|
+
uploadBlockedError: 'VIBE_REAL_UPLOAD_NOT_READY',
|
|
16041
|
+
},
|
|
16025
16042
|
terminalSuccess: 'uploaded',
|
|
16026
16043
|
stages: ['precheck', 'packaging', 'prepared', 'creating_task', 'waiting_authorization', 'uploading', 'uploaded', 'failed', 'cancelled'],
|
|
16027
16044
|
uploadSteps: ['avatar', 'app_info', 'package'],
|
|
@@ -16034,6 +16051,9 @@ function getCapabilities() {
|
|
|
16034
16051
|
metadataForm: false,
|
|
16035
16052
|
dryRun: true,
|
|
16036
16053
|
mock: true,
|
|
16054
|
+
mockRequiresBackend: true,
|
|
16055
|
+
mockBackendRouting: 'built-in-ppe-task-only',
|
|
16056
|
+
mockRequiresEnvironmentConfiguration: false,
|
|
16037
16057
|
mockAutoAuthorization: true,
|
|
16038
16058
|
mockRequiresClientScan: false,
|
|
16039
16059
|
mockSimulatedProgress: true,
|
|
@@ -16980,7 +17000,7 @@ class VibeError extends Error {
|
|
|
16980
17000
|
}
|
|
16981
17001
|
}
|
|
16982
17002
|
|
|
16983
|
-
// BFF IDL: feat-minis-import
|
|
17003
|
+
// BFF IDL: feat-minis-import. PPE task creation/polling only; no production adapter.
|
|
16984
17004
|
const prefix = '/bff/app_info/minis_import/';
|
|
16985
17005
|
const MINIS_IMPORT_PATHS = {
|
|
16986
17006
|
create: `${prefix}create_minis_import_task_for_agent/`,
|
|
@@ -17020,7 +17040,7 @@ function status(value) {
|
|
|
17020
17040
|
}
|
|
17021
17041
|
function envelope(data) {
|
|
17022
17042
|
if (!data || data.error_info?.code !== 0) {
|
|
17023
|
-
if (data?.error_info?.code === 400001)
|
|
17043
|
+
if (data?.error_info?.code === 400001 || data?.error_info?.code === 420001)
|
|
17024
17044
|
throw new VibeError('VIBE_AUTH_EXPIRED');
|
|
17025
17045
|
if (typeof data?.error_info?.code === 'number')
|
|
17026
17046
|
throw new VibeError('VIBE_BACKEND_REJECTED');
|
|
@@ -17136,13 +17156,87 @@ function createLocalHttpBackend(baseUrl) {
|
|
|
17136
17156
|
base.search ||
|
|
17137
17157
|
base.hash)
|
|
17138
17158
|
throw new VibeError('VIBE_INVALID_LOCAL_ENDPOINT');
|
|
17159
|
+
return createHttpBackend('local-http', base.origin);
|
|
17160
|
+
}
|
|
17161
|
+
function createPpeBackend(lane, adapter) {
|
|
17162
|
+
// No default lane and no production fallback. Keep PPE requests on the known host.
|
|
17163
|
+
if (!lane || !/^ppe_[a-zA-Z0-9_-]{1,64}$/.test(lane))
|
|
17164
|
+
throw new VibeError('VIBE_INVALID_PPE_ENV');
|
|
17165
|
+
const backend = createHttpBackend('ppe', 'https://developers.tiktok.com', {
|
|
17166
|
+
'x-use-ppe': '1',
|
|
17167
|
+
'x-tt-env': lane,
|
|
17168
|
+
}, adapter);
|
|
17169
|
+
const uploadUnavailable = async () => {
|
|
17170
|
+
throw new VibeError('VIBE_REAL_UPLOAD_NOT_READY');
|
|
17171
|
+
};
|
|
17172
|
+
return {
|
|
17173
|
+
...backend,
|
|
17174
|
+
uploadAvailable: false,
|
|
17175
|
+
uploadIcon: uploadUnavailable,
|
|
17176
|
+
submitInfo: uploadUnavailable,
|
|
17177
|
+
uploadPackage: uploadUnavailable,
|
|
17178
|
+
};
|
|
17179
|
+
}
|
|
17180
|
+
/** Opt-in integration demo: only creation and polling can reach PPE. */
|
|
17181
|
+
// Release-owned configuration, not a developer setup step. Remove PPE routing
|
|
17182
|
+
// here when the production chain is ready; do not change the Skill command.
|
|
17183
|
+
function createVibeTrialBackend(scenario, adapter) {
|
|
17184
|
+
return createPpeMockBackend('ppe_op_vc', adapter, scenario);
|
|
17185
|
+
}
|
|
17186
|
+
function createPpeMockBackend(lane, adapter, scenario = 'success') {
|
|
17187
|
+
const real = createPpeBackend(lane, adapter);
|
|
17188
|
+
const mock = createMockBackend(scenario);
|
|
17189
|
+
let realTask;
|
|
17190
|
+
let mockTask;
|
|
17191
|
+
let polls = 0;
|
|
17192
|
+
let simulatedReady = false;
|
|
17193
|
+
const uploadTask = (task) => {
|
|
17194
|
+
if (task !== realTask || !mockTask || !simulatedReady)
|
|
17195
|
+
throw new VibeError('VIBE_TASK_NOT_ACCEPTING_INPUT');
|
|
17196
|
+
return mockTask;
|
|
17197
|
+
};
|
|
17198
|
+
return {
|
|
17199
|
+
mode: 'ppe-mock',
|
|
17200
|
+
uploadAvailable: true,
|
|
17201
|
+
async createTask(input, signal) {
|
|
17202
|
+
if (realTask)
|
|
17203
|
+
throw new VibeError('VIBE_TASK_NOT_ACCEPTING_INPUT');
|
|
17204
|
+
realTask = await real.createTask(input, signal);
|
|
17205
|
+
// Separate in-memory credentials: real import tokens never reach mock uploads.
|
|
17206
|
+
mockTask = await mock.createTask(input, signal);
|
|
17207
|
+
return realTask;
|
|
17208
|
+
},
|
|
17209
|
+
async getTaskStatus(task, signal) {
|
|
17210
|
+
if (task !== realTask)
|
|
17211
|
+
throw new VibeError('VIBE_TASK_NOT_ACCEPTING_INPUT');
|
|
17212
|
+
// Errors remain errors; this is not an automatic fallback for failed requests.
|
|
17213
|
+
const observed = await real.getTaskStatus(task, signal);
|
|
17214
|
+
if (++polls >= 3) {
|
|
17215
|
+
const simulated = await mock.getTaskStatus(mockTask, signal);
|
|
17216
|
+
// The mock normal path needs three observations before accepting uploads.
|
|
17217
|
+
if (!simulated.readyForInput) {
|
|
17218
|
+
await mock.getTaskStatus(mockTask, signal);
|
|
17219
|
+
simulatedReady = (await mock.getTaskStatus(mockTask, signal)).readyForInput;
|
|
17220
|
+
}
|
|
17221
|
+
else
|
|
17222
|
+
simulatedReady = true;
|
|
17223
|
+
}
|
|
17224
|
+
return { ...observed, simulatedReadyForInput: simulatedReady };
|
|
17225
|
+
},
|
|
17226
|
+
uploadIcon: async (task, input, signal) => mock.uploadIcon(uploadTask(task), input, signal),
|
|
17227
|
+
submitInfo: async (task, title, iconUri, signal) => mock.submitInfo(uploadTask(task), title, iconUri, signal),
|
|
17228
|
+
uploadPackage: async (task, input, signal, onProgress) => mock.uploadPackage(uploadTask(task), input, signal, onProgress),
|
|
17229
|
+
};
|
|
17230
|
+
}
|
|
17231
|
+
function createHttpBackend(mode, baseURL, environmentHeaders = {}, adapter) {
|
|
17139
17232
|
const client = axios.create({
|
|
17140
|
-
baseURL
|
|
17233
|
+
baseURL,
|
|
17141
17234
|
timeout: 30000,
|
|
17142
17235
|
maxRedirects: 0,
|
|
17143
17236
|
proxy: false,
|
|
17237
|
+
adapter,
|
|
17144
17238
|
});
|
|
17145
|
-
return createBackend(
|
|
17239
|
+
return createBackend(mode, async (request) => {
|
|
17146
17240
|
try {
|
|
17147
17241
|
let body;
|
|
17148
17242
|
let headers = {};
|
|
@@ -17169,7 +17263,7 @@ function createLocalHttpBackend(baseUrl) {
|
|
|
17169
17263
|
url: request.path,
|
|
17170
17264
|
params: request.method === 'GET' ? request.data : undefined,
|
|
17171
17265
|
data: body,
|
|
17172
|
-
headers,
|
|
17266
|
+
headers: { ...environmentHeaders, ...headers },
|
|
17173
17267
|
signal: request.signal,
|
|
17174
17268
|
maxBodyLength: Infinity,
|
|
17175
17269
|
onUploadProgress: p => request.onProgress?.(p.loaded, p.total),
|
|
@@ -17273,6 +17367,11 @@ function createMockBackend(scenario = 'success') {
|
|
|
17273
17367
|
});
|
|
17274
17368
|
}
|
|
17275
17369
|
|
|
17370
|
+
function createAuthorizationScheme(landingUrl) {
|
|
17371
|
+
// Encode the entire landing URL once so its token/query stays inside the WebView.
|
|
17372
|
+
return `aweme://webview?url=${encodeURIComponent(safeWebUrl(landingUrl))}`;
|
|
17373
|
+
}
|
|
17374
|
+
|
|
17276
17375
|
const DEV_PORT = 3000;
|
|
17277
17376
|
const DEV_WS_PORT = 4000;
|
|
17278
17377
|
path.join(os.homedir(), '__TTMG__');
|
|
@@ -17573,52 +17672,60 @@ async function renderVibePage(input) {
|
|
|
17573
17672
|
const copy = zh
|
|
17574
17673
|
? {
|
|
17575
17674
|
steps: ['扫码授权', '上传游戏', '完成'],
|
|
17576
|
-
mock: '模拟',
|
|
17675
|
+
mock: input.hybrid ? 'PPE + Mock' : '模拟',
|
|
17577
17676
|
game: '当前游戏',
|
|
17578
17677
|
gameIcon: '游戏头像',
|
|
17579
17678
|
bindingTitle: '上传到 TikTok',
|
|
17580
|
-
bindingAction: '使用 TikTok 扫描二维码',
|
|
17581
|
-
bindingSubtitle:
|
|
17582
|
-
|
|
17679
|
+
bindingAction: input.hybrid ? '真实授权二维码 · 混合联调' : '使用 TikTok 扫描二维码',
|
|
17680
|
+
bindingSubtitle: input.hybrid
|
|
17681
|
+
? '可扫码查看端内授权页。查询三次真实状态后,模拟授权和上传,无需完成手机授权。'
|
|
17682
|
+
: input.uploadAvailable === false
|
|
17683
|
+
? '请在手机上完成授权。当前为真实 PPE 授权联调,尚未开放上传。'
|
|
17684
|
+
: '在手机上确认授权后,将继续上传这个游戏。',
|
|
17685
|
+
bindingStatus: input.hybrid ? '正在查询 PPE 任务' : input.simulation ? '模拟等待扫码' : '等待扫码',
|
|
17583
17686
|
uploadingTitle: '正在上传小游戏',
|
|
17584
|
-
uploadingSubtitle: '授权已完成,正在上传本次游戏内容。',
|
|
17687
|
+
uploadingSubtitle: input.simulation ? '正在模拟授权后的上传流程,不会上传游戏内容。' : '授权已完成,正在上传本次游戏内容。',
|
|
17585
17688
|
uploadLabel: input.simulation ? '模拟上传进度' : '上传进度',
|
|
17586
17689
|
uploadStatus: '正在上传游戏,请稍候',
|
|
17587
|
-
completeTitle: '上传完成',
|
|
17690
|
+
completeTitle: input.simulation ? '模拟上传完成' : '上传完成',
|
|
17588
17691
|
completeSubtitle: input.simulation
|
|
17589
17692
|
? '绑定和上传流程模拟完成,未向 TikTok 上传游戏。'
|
|
17590
17693
|
: '游戏内容已上传,本次操作已完成。',
|
|
17591
17694
|
failedTitle: '本次上传未完成',
|
|
17592
17695
|
bindingCode: '授权绑定二维码',
|
|
17593
|
-
accepted: '已上传',
|
|
17594
|
-
mockNote: '模拟运行 · 无需扫码,不会真实上传',
|
|
17696
|
+
accepted: input.simulation ? '模拟完成' : '已上传',
|
|
17697
|
+
mockNote: input.hybrid ? '真实创建 / 查询任务 · 授权 / 上传为模拟 · 不改变真实任务状态' : '模拟运行 · 无需扫码,不会真实上传',
|
|
17595
17698
|
footer: '临时页面 · 15 分钟后关闭',
|
|
17596
17699
|
}
|
|
17597
17700
|
: {
|
|
17598
17701
|
steps: ['Authorize', 'Upload', 'Done'],
|
|
17599
|
-
mock: 'MOCK',
|
|
17702
|
+
mock: input.hybrid ? 'PPE + Mock' : 'MOCK',
|
|
17600
17703
|
game: 'YOUR GAME',
|
|
17601
17704
|
gameIcon: 'Game icon',
|
|
17602
17705
|
bindingTitle: 'Upload to TikTok',
|
|
17603
|
-
bindingAction: 'Scan the QR code with TikTok',
|
|
17604
|
-
bindingSubtitle:
|
|
17605
|
-
|
|
17706
|
+
bindingAction: input.hybrid ? 'Real authorization QR · integration demo' : 'Scan the QR code with TikTok',
|
|
17707
|
+
bindingSubtitle: input.hybrid
|
|
17708
|
+
? 'Scan to view the in-app page. After three real status polls, authorization and upload are simulated. Phone authorization is not required.'
|
|
17709
|
+
: input.uploadAvailable === false
|
|
17710
|
+
? 'Authorize on your phone. This is a real PPE authorization test; upload is not enabled yet.'
|
|
17711
|
+
: 'Authorize on your phone to continue uploading this game.',
|
|
17712
|
+
bindingStatus: input.hybrid ? 'Querying the PPE task' : input.simulation
|
|
17606
17713
|
? 'Simulating scan request'
|
|
17607
17714
|
: 'Waiting for you to scan',
|
|
17608
17715
|
uploadingTitle: 'Uploading your game',
|
|
17609
|
-
uploadingSubtitle: 'Account authorized. Your game is being uploaded.',
|
|
17716
|
+
uploadingSubtitle: input.simulation ? 'Simulating the post-authorization upload. No game content is sent.' : 'Account authorized. Your game is being uploaded.',
|
|
17610
17717
|
uploadLabel: input.simulation
|
|
17611
17718
|
? 'Simulated upload progress'
|
|
17612
17719
|
: 'Upload progress',
|
|
17613
17720
|
uploadStatus: 'Uploading your game. Please wait.',
|
|
17614
|
-
completeTitle: 'Upload complete',
|
|
17721
|
+
completeTitle: input.simulation ? 'Simulated upload complete' : 'Upload complete',
|
|
17615
17722
|
completeSubtitle: input.simulation
|
|
17616
17723
|
? 'Binding and upload simulation complete. Nothing was uploaded to TikTok.'
|
|
17617
17724
|
: 'Your game content has been uploaded. This operation is complete.',
|
|
17618
17725
|
failedTitle: 'Upload not completed',
|
|
17619
17726
|
bindingCode: 'Authorization QR',
|
|
17620
|
-
accepted: 'Uploaded',
|
|
17621
|
-
mockNote: 'Mock only · no scan needed, nothing is uploaded',
|
|
17727
|
+
accepted: input.simulation ? 'Simulation complete' : 'Uploaded',
|
|
17728
|
+
mockNote: input.hybrid ? 'Real task creation / polling · Mock authorization / upload · Real task status unchanged' : 'Mock only · no scan needed, nothing is uploaded',
|
|
17622
17729
|
footer: 'Temporary page · closes after 15 minutes',
|
|
17623
17730
|
};
|
|
17624
17731
|
const step = phase === 'binding' ? 0 : phase === 'uploading' ? 1 : 2;
|
|
@@ -17887,6 +17994,7 @@ function abortableDelay(ms, signal) {
|
|
|
17887
17994
|
});
|
|
17888
17995
|
}
|
|
17889
17996
|
const defaults = {
|
|
17997
|
+
createTrialBackend: createVibeTrialBackend,
|
|
17890
17998
|
prepare: prepareVibeInput,
|
|
17891
17999
|
startPage: startVibePage,
|
|
17892
18000
|
sleep: abortableDelay,
|
|
@@ -17905,6 +18013,9 @@ async function executeVibeUpload(options, onEvent = () => { }, dependencies = {}
|
|
|
17905
18013
|
let page;
|
|
17906
18014
|
let detached = false;
|
|
17907
18015
|
let progressUpdates = Promise.resolve();
|
|
18016
|
+
let ppeLane;
|
|
18017
|
+
let ppeSelected = false;
|
|
18018
|
+
let hybridSelected = false;
|
|
17908
18019
|
let result = {
|
|
17909
18020
|
schemaVersion: '2',
|
|
17910
18021
|
command: 'upload',
|
|
@@ -17939,14 +18050,24 @@ async function executeVibeUpload(options, onEvent = () => { }, dependencies = {}
|
|
|
17939
18050
|
throw new VibeError('VIBE_CANCELLED');
|
|
17940
18051
|
};
|
|
17941
18052
|
try {
|
|
17942
|
-
//
|
|
17943
|
-
//
|
|
18053
|
+
// --mock is the stable trial entry: working steps use the built-in PPE lane,
|
|
18054
|
+
// blocked steps remain simulated. No environment setup is required.
|
|
18055
|
+
// Explicit command-line selection wins; real failures never fall back to mock.
|
|
17944
18056
|
if (!options.mock && !options.vibeApi && process.env.TTMG_VIBE_BACKEND) {
|
|
17945
18057
|
const configured = process.env.TTMG_VIBE_BACKEND;
|
|
17946
18058
|
if (configured === 'mock')
|
|
17947
18059
|
options = { ...options, mock: true };
|
|
17948
18060
|
else if (configured === 'local-http' && process.env.TTMG_VIBE_API)
|
|
17949
18061
|
options = { ...options, vibeApi: process.env.TTMG_VIBE_API };
|
|
18062
|
+
else if (configured === 'ppe' || configured === 'ppe-mock') {
|
|
18063
|
+
if (process.env.TTMG_VIBE_API)
|
|
18064
|
+
throw new VibeError('VIBE_BACKEND_OPTION_CONFLICT');
|
|
18065
|
+
ppeSelected = true;
|
|
18066
|
+
hybridSelected = configured === 'ppe-mock';
|
|
18067
|
+
ppeLane = process.env.TTMG_VIBE_PPE;
|
|
18068
|
+
if (!ppeLane || !/^ppe_[a-zA-Z0-9_-]{1,64}$/.test(ppeLane))
|
|
18069
|
+
throw new VibeError('VIBE_INVALID_PPE_ENV');
|
|
18070
|
+
}
|
|
17950
18071
|
else
|
|
17951
18072
|
throw new VibeError('VIBE_BACKEND_NOT_CONFIGURED');
|
|
17952
18073
|
}
|
|
@@ -17960,7 +18081,7 @@ async function executeVibeUpload(options, onEvent = () => { }, dependencies = {}
|
|
|
17960
18081
|
if (options.vibeAuthorizeUrl)
|
|
17961
18082
|
throw new VibeError('VIBE_DEPRECATED_OPTION');
|
|
17962
18083
|
if ((options.mock && (options.vibeApi || options.vibeAuthorizeUrl)) ||
|
|
17963
|
-
(!options.mock && options.mockScenario))
|
|
18084
|
+
(!options.mock && !hybridSelected && options.mockScenario))
|
|
17964
18085
|
throw new VibeError('VIBE_BACKEND_OPTION_CONFLICT');
|
|
17965
18086
|
if (options.pollInterval !== undefined)
|
|
17966
18087
|
seconds(options.pollInterval, 1);
|
|
@@ -17972,7 +18093,11 @@ async function executeVibeUpload(options, onEvent = () => { }, dependencies = {}
|
|
|
17972
18093
|
let backend = deps.backend;
|
|
17973
18094
|
if (!options.dryRun && !backend) {
|
|
17974
18095
|
if (options.mock)
|
|
17975
|
-
backend =
|
|
18096
|
+
backend = deps.createTrialBackend(options.mockScenario);
|
|
18097
|
+
else if (hybridSelected)
|
|
18098
|
+
backend = createPpeMockBackend(ppeLane, undefined, options.mockScenario);
|
|
18099
|
+
else if (ppeSelected)
|
|
18100
|
+
backend = createPpeBackend(ppeLane);
|
|
17976
18101
|
else if (options.vibeApi)
|
|
17977
18102
|
backend = createLocalHttpBackend(options.vibeApi);
|
|
17978
18103
|
else
|
|
@@ -17980,7 +18105,8 @@ async function executeVibeUpload(options, onEvent = () => { }, dependencies = {}
|
|
|
17980
18105
|
}
|
|
17981
18106
|
emit('precheck', {
|
|
17982
18107
|
backend: backend?.mode || 'none',
|
|
17983
|
-
simulation: backend?.mode === 'mock',
|
|
18108
|
+
simulation: backend?.mode === 'mock' || backend?.mode === 'ppe-mock',
|
|
18109
|
+
...(backend?.mode === 'ppe-mock' ? { simulationStartsAt: 'authorization' } : {}),
|
|
17984
18110
|
});
|
|
17985
18111
|
ensureActive();
|
|
17986
18112
|
const input = await deps.prepare(options, () => emit('packaging'), controller.signal);
|
|
@@ -18000,9 +18126,11 @@ async function executeVibeUpload(options, onEvent = () => { }, dependencies = {}
|
|
|
18000
18126
|
const pageGame = {
|
|
18001
18127
|
title: input.title,
|
|
18002
18128
|
iconDataUrl: `data:${input.icon.contentType};base64,${input.icon.bytes.toString('base64')}`,
|
|
18129
|
+
uploadAvailable: backend.uploadAvailable !== false,
|
|
18130
|
+
hybrid: backend.mode === 'ppe-mock',
|
|
18003
18131
|
};
|
|
18004
18132
|
const sourceProjectId = deps.projectId(options);
|
|
18005
|
-
emit('creating_task');
|
|
18133
|
+
emit('creating_task', { platformWrite: backend.mode === 'ppe' || backend.mode === 'ppe-mock' });
|
|
18006
18134
|
const task = await backend.createTask({
|
|
18007
18135
|
source_type: 1,
|
|
18008
18136
|
source_project_id: sourceProjectId,
|
|
@@ -18010,7 +18138,7 @@ async function executeVibeUpload(options, onEvent = () => { }, dependencies = {}
|
|
|
18010
18138
|
minis_type: 'MINI_GAME',
|
|
18011
18139
|
}, controller.signal);
|
|
18012
18140
|
ensureActive();
|
|
18013
|
-
const authorizationUrl =
|
|
18141
|
+
const authorizationUrl = createAuthorizationScheme(task.landingUrl);
|
|
18014
18142
|
const pollMs = seconds(options.pollInterval, task.pollIntervalSeconds);
|
|
18015
18143
|
result = {
|
|
18016
18144
|
...result,
|
|
@@ -18019,17 +18147,25 @@ async function executeVibeUpload(options, onEvent = () => { }, dependencies = {}
|
|
|
18019
18147
|
};
|
|
18020
18148
|
page = await deps.startPage();
|
|
18021
18149
|
ensureActive();
|
|
18150
|
+
const bindingMessage = backend.mode === 'ppe-mock'
|
|
18151
|
+
? (getCurrentLanguage() === 'zh-CN'
|
|
18152
|
+
? '真实 PPE 任务和授权二维码;查询三次后模拟授权、上传,无需完成手机授权。'
|
|
18153
|
+
: 'Real PPE task and authorization QR. After three polls, authorization and upload are simulated; phone authorization is not required.')
|
|
18154
|
+
: backend.uploadAvailable === false
|
|
18155
|
+
? (getCurrentLanguage() === 'zh-CN' ? '请扫码完成真实 PPE 授权;上传尚未开放。' : 'Scan to authorize in PPE. Upload is not enabled yet.')
|
|
18156
|
+
: text('waiting_authorization', result.simulation);
|
|
18022
18157
|
await page.update({
|
|
18023
18158
|
...pageGame,
|
|
18024
18159
|
language: getCurrentLanguage(),
|
|
18025
18160
|
phase: 'binding',
|
|
18026
18161
|
simulation: result.simulation,
|
|
18027
|
-
message:
|
|
18162
|
+
message: bindingMessage,
|
|
18028
18163
|
qrUrl: authorizationUrl,
|
|
18029
18164
|
purpose: 'authorization_binding',
|
|
18030
18165
|
});
|
|
18031
18166
|
ensureActive();
|
|
18032
18167
|
emit('waiting_authorization', {
|
|
18168
|
+
message: bindingMessage,
|
|
18033
18169
|
pageUrl: page.url,
|
|
18034
18170
|
qrPurpose: 'authorization_binding',
|
|
18035
18171
|
nextAction: result.simulation ? 'wait' : 'scan_qr',
|
|
@@ -18046,11 +18182,17 @@ async function executeVibeUpload(options, onEvent = () => { }, dependencies = {}
|
|
|
18046
18182
|
...result,
|
|
18047
18183
|
taskStatusRaw: response.statusRaw,
|
|
18048
18184
|
readyForInput: response.readyForInput,
|
|
18185
|
+
...(backend.mode === 'ppe-mock' ? {
|
|
18186
|
+
simulatedReadyForInput: response.simulatedReadyForInput === true,
|
|
18187
|
+
simulatedTaskStatusRaw: response.simulatedReadyForInput ? 2 : 1,
|
|
18188
|
+
} : {}),
|
|
18049
18189
|
};
|
|
18050
|
-
if (response.readyForInput)
|
|
18190
|
+
if (backend.mode === 'ppe-mock' ? response.simulatedReadyForInput : response.readyForInput)
|
|
18051
18191
|
break;
|
|
18052
18192
|
await deps.sleep(pollMs, controller.signal);
|
|
18053
18193
|
}
|
|
18194
|
+
if (backend.uploadAvailable === false)
|
|
18195
|
+
throw new VibeError('VIBE_REAL_UPLOAD_NOT_READY');
|
|
18054
18196
|
await page.update({
|
|
18055
18197
|
...pageGame,
|
|
18056
18198
|
language: getCurrentLanguage(),
|
|
@@ -18105,7 +18247,9 @@ async function executeVibeUpload(options, onEvent = () => { }, dependencies = {}
|
|
|
18105
18247
|
task.importToken = '';
|
|
18106
18248
|
task.landingUrl = '';
|
|
18107
18249
|
// A validated upload receipt is authoritative even if result-page rendering fails.
|
|
18108
|
-
result = { ...result, uploaded: true,
|
|
18250
|
+
result = { ...result, uploaded: true, ...(backend.mode === 'ppe-mock'
|
|
18251
|
+
? { simulatedTaskStatusRaw: receipt.statusRaw }
|
|
18252
|
+
: { taskStatusRaw: receipt.statusRaw }) };
|
|
18109
18253
|
await progressUpdates;
|
|
18110
18254
|
if (progressPageError)
|
|
18111
18255
|
throw progressPageError;
|
|
@@ -18160,6 +18304,14 @@ async function executeVibeUpload(options, onEvent = () => { }, dependencies = {}
|
|
|
18160
18304
|
'尚未配置服务;本地模拟请使用 --mock。',
|
|
18161
18305
|
'No backend configured. Use --mock for local simulation.',
|
|
18162
18306
|
],
|
|
18307
|
+
VIBE_INVALID_PPE_ENV: [
|
|
18308
|
+
'PPE 模式需要显式设置 TTMG_VIBE_PPE,例如 ppe_op_vc。',
|
|
18309
|
+
'PPE mode requires TTMG_VIBE_PPE, for example ppe_op_vc.',
|
|
18310
|
+
],
|
|
18311
|
+
VIBE_REAL_UPLOAD_NOT_READY: [
|
|
18312
|
+
'任务授权已完成,尚未上传:真实上传所需的完整基础信息尚未接入。不会模拟上传成功。',
|
|
18313
|
+
'Task authorized; nothing uploaded. The complete app-info input contract is not integrated yet. No simulated success.',
|
|
18314
|
+
],
|
|
18163
18315
|
VIBE_BACKEND_REQUEST_FAILED: [
|
|
18164
18316
|
'服务请求失败,本次操作已停止;已开始上传时请先核对任务,勿直接重传。',
|
|
18165
18317
|
'The service request failed. If upload started, check the task before retrying.',
|
|
@@ -18200,6 +18352,7 @@ async function executeVibeUpload(options, onEvent = () => { }, dependencies = {}
|
|
|
18200
18352
|
title: result.title || '',
|
|
18201
18353
|
language: getCurrentLanguage(),
|
|
18202
18354
|
simulation: result.simulation,
|
|
18355
|
+
hybrid: result.backend === 'ppe-mock',
|
|
18203
18356
|
phase: result.uploaded ? 'complete' : 'failed',
|
|
18204
18357
|
message,
|
|
18205
18358
|
});
|
|
@@ -18255,7 +18408,7 @@ async function vibeUpload(options) {
|
|
|
18255
18408
|
else if (format === 'text')
|
|
18256
18409
|
console.log(`${event.simulation ? '[MOCK] ' : ''}${event.message}${event.status === 'uploading' && percent(event.loadedBytes, event.totalBytes) !== undefined ? ` ${percent(event.loadedBytes, event.totalBytes)}%` : ''}${event.pageUrl && ['waiting_authorization', 'uploaded'].includes(event.status) ? `\n${event.pageUrl}` : ''}${event.errorCode ? ` (${event.errorCode})` : ''}`);
|
|
18257
18410
|
else if (event.status === 'waiting_authorization')
|
|
18258
|
-
console.error(`${event.simulation ? '[MOCK] ' : ''}${
|
|
18411
|
+
console.error(`${event.simulation ? '[MOCK] ' : ''}${event.message}\n${event.pageUrl}`);
|
|
18259
18412
|
}, {}, controller.signal);
|
|
18260
18413
|
if (format === 'json' || !['text', 'json', 'ndjson'].includes(format))
|
|
18261
18414
|
console.log(JSON.stringify(result));
|