@ttmg/cli 0.4.6-vibe-beta.15 → 0.4.6-vibe-beta.16
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 +10 -30
- package/dist/index.js +164 -109
- package/dist/index.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/scripts/verify-agent-contract.js +3 -2
- package/dist/scripts/vibe-upload-smoke.js +33 -18
- package/package.json +1 -1
- package/vibe-upload.md +85 -273
package/dist/index.js
CHANGED
|
@@ -7241,8 +7241,8 @@ const messages = {
|
|
|
7241
7241
|
'cli.command.login.password': 'Developer account password (implicit mode)',
|
|
7242
7242
|
'cli.command.logout.desc': 'Logout and clear local user data',
|
|
7243
7243
|
'cli.command.auth.desc': 'Inspect local authentication state',
|
|
7244
|
-
'cli.command.auth.open.desc': '
|
|
7245
|
-
'cli.command.upload.vibeWeb': '
|
|
7244
|
+
'cli.command.auth.open.desc': 'Reopen the local QR page for an existing Vibe task',
|
|
7245
|
+
'cli.command.upload.vibeWeb': 'Disabled; use --vibe and scan with TikTok on your phone',
|
|
7246
7246
|
'cli.command.game.desc': 'Inspect game metadata supported by the platform',
|
|
7247
7247
|
'cli.command.game.categories.desc': 'List game categories and subcategories with gameplay descriptions (offline, read-only)',
|
|
7248
7248
|
'cli.command.game.categories.category': 'Filter by an exact category value, e.g. Puzzle',
|
|
@@ -7252,23 +7252,24 @@ const messages = {
|
|
|
7252
7252
|
'cli.command.upload.vibeDescription': 'Optional game description (--vibe); default: Discover the fun of TikTok Mini Games.',
|
|
7253
7253
|
'cli.command.upload.vibeSubcategory': 'Optional subcategory belonging to --category; omit both to keep Other/Other (--vibe)',
|
|
7254
7254
|
'cli.command.upload.vibeWebGuide': `
|
|
7255
|
-
Vibe
|
|
7256
|
-
ttmg upload --vibe --
|
|
7255
|
+
Vibe QR upload (real PPE trial):
|
|
7256
|
+
ttmg upload --vibe --dir ./game --title "My game" --icon ./avatar.png --format ndjson
|
|
7257
7257
|
Use --package ./game.zip instead of --dir for an existing ZIP.
|
|
7258
7258
|
Collect only title and icon; the CLI fills other app_info fields with PPE trial defaults.
|
|
7259
7259
|
Skill may pass --description "Game description"; otherwise use "Discover the fun of TikTok Mini Games." (legacy metadata-file descriptions are preserved).
|
|
7260
7260
|
Skill may query ttmg game categories --format json and pass --category Puzzle --subcategory Physics together.
|
|
7261
7261
|
Omit both to keep Other/Other (or the legacy metadata-file classification). Invalid pairs fail before task creation.
|
|
7262
7262
|
prepared and terminal events expose the submitted classification and its source.
|
|
7263
|
-
Keep this process running. At waiting_authorization,
|
|
7264
|
-
pageUrl is the local
|
|
7263
|
+
Keep this process running. At waiting_authorization, scan the QR on pageUrl with TikTok on your phone.
|
|
7264
|
+
pageUrl is the local QR/progress page on the CLI computer. Reopen it with authorizationOpenCommand.
|
|
7265
7265
|
The CLI polls and uploads avatar, app_info and package automatically after authorization. Do not start a second upload.
|
|
7266
7266
|
Real success: terminal=true, status=uploaded, uploaded=true, simulation=false. On failure, read errorCode and nextAction.
|
|
7267
|
-
|
|
7267
|
+
The local QR page opens for every output format; --no-open disables automatic opening.
|
|
7268
|
+
The page displays the submitted title, icon, description, category and subcategory.
|
|
7268
7269
|
Upload completion is not review approval or production release. See bundled vibe-upload.md and ttmg capabilities --format json.
|
|
7269
7270
|
`,
|
|
7270
7271
|
'cli.command.auth.open.pageUrl': 'Local pageUrl returned by the running Vibe upload command',
|
|
7271
|
-
'cli.command.auth.open.requested': '
|
|
7272
|
+
'cli.command.auth.open.requested': 'QR page open requested. Scan with TikTok on your phone; the existing upload process continues polling.',
|
|
7272
7273
|
'cli.command.auth.open.invalidPage': 'Pass the local pageUrl from the running Vibe upload command, not the platform URL.',
|
|
7273
7274
|
'cli.command.auth.open.unavailable': 'Authorization page is unavailable, expired, or no longer waiting. Check the existing upload process; no new task was created.',
|
|
7274
7275
|
'cli.command.auth.open.invalidFormat': 'Use text, json, or ndjson output.',
|
|
@@ -7467,9 +7468,9 @@ Vibe web upload (real PPE trial):
|
|
|
7467
7468
|
'cli.command.upload.dir': 'Game project directory (default: current directory)',
|
|
7468
7469
|
'cli.command.upload.preview': 'Wait for platform processing and output the preview payload',
|
|
7469
7470
|
'cli.command.upload.servePreviewPage': 'Serve a temporary local preview page with test-account and preview QR codes',
|
|
7470
|
-
'cli.command.upload.noOpen': 'Do not open the local preview
|
|
7471
|
+
'cli.command.upload.noOpen': 'Do not automatically open the local preview or Vibe QR page',
|
|
7471
7472
|
'cli.command.upload.format': 'Output format: text | json | ndjson',
|
|
7472
|
-
'cli.command.upload.vibe': '
|
|
7473
|
+
'cli.command.upload.vibe': 'Scan with TikTok and upload: provide title and icon; CLI fills other fields with PPE trial defaults (schema v2)',
|
|
7473
7474
|
'cli.command.upload.vibeTitle': 'Game title supplied by the Skill (--vibe)',
|
|
7474
7475
|
'cli.command.upload.vibeAppInfo': 'Legacy metadata file compatibility; not needed for current uploads and must not be requested from users',
|
|
7475
7476
|
'cli.command.upload.vibeIcon': 'Local PNG/JPEG/WebP selected by the user (--vibe)',
|
|
@@ -7688,8 +7689,8 @@ Vibe web upload (real PPE trial):
|
|
|
7688
7689
|
'cli.command.login.password': '开发者账号密码(implicit 模式)',
|
|
7689
7690
|
'cli.command.logout.desc': '退出登录并清除本地用户信息',
|
|
7690
7691
|
'cli.command.auth.desc': '查看本地登录状态',
|
|
7691
|
-
'cli.command.auth.open.desc': '
|
|
7692
|
-
'cli.command.upload.vibeWeb': '
|
|
7692
|
+
'cli.command.auth.open.desc': '重新打开已有 Vibe 任务的本地二维码页',
|
|
7693
|
+
'cli.command.upload.vibeWeb': '已关闭;使用 --vibe 并在手机 TikTok 中扫码授权',
|
|
7693
7694
|
'cli.command.game.desc': '查询平台支持的游戏资料',
|
|
7694
7695
|
'cli.command.game.categories.desc': '查询游戏类型、子类型及玩法说明(离线、只读)',
|
|
7695
7696
|
'cli.command.game.categories.category': '按类型的精确值筛选,例如 Puzzle',
|
|
@@ -7699,23 +7700,24 @@ Vibe web upload (real PPE trial):
|
|
|
7699
7700
|
'cli.command.upload.vibeDescription': '可选游戏描述(--vibe);默认值:Discover the fun of TikTok Mini Games.',
|
|
7700
7701
|
'cli.command.upload.vibeSubcategory': '所属类型的可选子类型;两者均不传保留 Other/Other(--vibe)',
|
|
7701
7702
|
'cli.command.upload.vibeWebGuide': `
|
|
7702
|
-
Vibe
|
|
7703
|
-
ttmg upload --vibe --
|
|
7703
|
+
Vibe 扫码上传(真实 PPE 试用):
|
|
7704
|
+
ttmg upload --vibe --dir ./game --title "游戏名称" --icon ./avatar.png --format ndjson
|
|
7704
7705
|
已有 ZIP 时,用 --package ./game.zip 替换 --dir。
|
|
7705
7706
|
只收集标题和头像;其他 app_info 字段由 CLI 使用 PPE 试用默认值补齐。
|
|
7706
7707
|
Skill 可传 --description "游戏描述";省略时使用 Discover the fun of TikTok Mini Games.(旧资料文件中的描述保留)。
|
|
7707
7708
|
Skill 可先查 ttmg game categories --format json,再成对传入 --category Puzzle --subcategory Physics。
|
|
7708
7709
|
两者均不传保留 Other/Other(旧资料文件调用保留文件分类);无效组合在创建任务前报错。
|
|
7709
7710
|
prepared 和终态事件返回最终 classification 及其来源。
|
|
7710
|
-
保持原进程运行。在 waiting_authorization
|
|
7711
|
-
pageUrl
|
|
7711
|
+
保持原进程运行。在 waiting_authorization 阶段,使用手机 TikTok 扫描 pageUrl 页面中的二维码。
|
|
7712
|
+
pageUrl 是 CLI 所在电脑的二维码/进度页;重新打开时,执行返回的 authorizationOpenCommand。
|
|
7712
7713
|
CLI 负责轮询,授权后自动上传 avatar、app_info、package,不再启动第二次上传。
|
|
7713
7714
|
真实成功:terminal=true、status=uploaded、uploaded=true、simulation=false。失败时读取 errorCode 和 nextAction。
|
|
7714
|
-
|
|
7715
|
+
所有输出格式都会打开本地二维码页;--no-open 可关闭自动打开。
|
|
7716
|
+
页面展示本次提交的标题、头像、描述、类型和子类型。
|
|
7715
7717
|
上传完成不代表审核通过或正式上线。详见包内 vibe-upload.md 和 ttmg capabilities --format json。
|
|
7716
7718
|
`,
|
|
7717
7719
|
'cli.command.auth.open.pageUrl': '正在运行的 Vibe 上传命令返回的本地 pageUrl',
|
|
7718
|
-
'cli.command.auth.open.requested': '
|
|
7720
|
+
'cli.command.auth.open.requested': '已请求打开二维码页。请用手机 TikTok 扫码授权,原上传进程继续轮询。',
|
|
7719
7721
|
'cli.command.auth.open.invalidPage': '请传入正在运行的 Vibe 上传命令返回的本地 pageUrl,不是平台授权链接。',
|
|
7720
7722
|
'cli.command.auth.open.unavailable': '授权页不可用、已过期或已不在等待授权。请查看原上传进程状态;未新建任务。',
|
|
7721
7723
|
'cli.command.auth.open.invalidFormat': '输出格式请使用 text、json 或 ndjson。',
|
|
@@ -7914,9 +7916,9 @@ Vibe 网页上传(真实 PPE 试用):
|
|
|
7914
7916
|
'cli.command.upload.dir': '小游戏项目根目录(默认为当前目录)',
|
|
7915
7917
|
'cli.command.upload.preview': '等待平台处理并输出预览二维码原始内容',
|
|
7916
7918
|
'cli.command.upload.servePreviewPage': '启动包含测试账号与预览二维码的临时本地页面',
|
|
7917
|
-
'cli.command.upload.noOpen': '
|
|
7919
|
+
'cli.command.upload.noOpen': '不自动打开本地预览页面或 Vibe 二维码页',
|
|
7918
7920
|
'cli.command.upload.format': '输出格式:text | json | ndjson',
|
|
7919
|
-
'cli.command.upload.vibe': '
|
|
7921
|
+
'cli.command.upload.vibe': 'TikTok 扫码上传:只收集标题和头像,CLI 自动补齐其他字段的 PPE 试用默认值(schema v2)',
|
|
7920
7922
|
'cli.command.upload.vibeAppInfo': '仅兼容旧资料文件;当前上传无需此参数,不应向用户索要',
|
|
7921
7923
|
'cli.command.upload.vibeTitle': 'Skill 提供的游戏标题(--vibe)',
|
|
7922
7924
|
'cli.command.upload.vibeIcon': '用户指定的本地 PNG/JPEG/WebP 图片(--vibe)',
|
|
@@ -16376,7 +16378,7 @@ async function authStatus(options = {}) {
|
|
|
16376
16378
|
return result;
|
|
16377
16379
|
}
|
|
16378
16380
|
|
|
16379
|
-
var version = "0.4.6-vibe-beta.
|
|
16381
|
+
var version = "0.4.6-vibe-beta.16";
|
|
16380
16382
|
var engines = {
|
|
16381
16383
|
node: ">=20.19.1 <21 || >=22"
|
|
16382
16384
|
};
|
|
@@ -17136,7 +17138,7 @@ function getCapabilities() {
|
|
|
17136
17138
|
networkRequired: false,
|
|
17137
17139
|
},
|
|
17138
17140
|
vibeUpload: {
|
|
17139
|
-
command: 'ttmg upload --vibe --
|
|
17141
|
+
command: 'ttmg upload --vibe --dir <gameDir> --title <title> --icon <iconPath> --format ndjson',
|
|
17140
17142
|
mockCommand: 'ttmg upload --vibe --mock --dir <gameDir> --title <title> --icon <iconPath> --format ndjson',
|
|
17141
17143
|
guide: 'vibe-upload.md',
|
|
17142
17144
|
option: '--vibe',
|
|
@@ -17159,7 +17161,7 @@ function getCapabilities() {
|
|
|
17159
17161
|
host: 'developers.tiktok.com',
|
|
17160
17162
|
taskCreation: true,
|
|
17161
17163
|
statusPolling: true,
|
|
17162
|
-
authorizationScheme: '
|
|
17164
|
+
authorizationScheme: 'landing_url',
|
|
17163
17165
|
uploadEnabled: true,
|
|
17164
17166
|
},
|
|
17165
17167
|
terminalSuccess: 'uploaded',
|
|
@@ -17186,7 +17188,27 @@ function getCapabilities() {
|
|
|
17186
17188
|
legacyAppInfo: 'preserved-when-options-omitted; explicit-pair-overrides-category-fields',
|
|
17187
17189
|
resultField: 'classification',
|
|
17188
17190
|
},
|
|
17189
|
-
webAuthorization: { option: '--web',
|
|
17191
|
+
webAuthorization: { option: '--web', available: false, errorCode: 'VIBE_WEB_DISABLED', replacement: '--vibe', materialUploadEnabled: false },
|
|
17192
|
+
qrAuthorization: {
|
|
17193
|
+
command: 'ttmg upload --vibe --dir <gameDir> --title <title> --icon <iconPath> --format ndjson',
|
|
17194
|
+
defaultWithoutWeb: true,
|
|
17195
|
+
backend: 'ppe',
|
|
17196
|
+
defaultLane: 'ppe_op_vc',
|
|
17197
|
+
requiresEnvironmentConfiguration: false,
|
|
17198
|
+
simulation: false,
|
|
17199
|
+
opensLocalQrPage: true,
|
|
17200
|
+
formatIndependent: true,
|
|
17201
|
+
disablePageOpenOption: '--no-open',
|
|
17202
|
+
pageUrlField: 'pageUrl',
|
|
17203
|
+
authorizationMode: 'qr',
|
|
17204
|
+
nextAction: 'scan_qr',
|
|
17205
|
+
userAction: 'Open pageUrl on the CLI computer and scan the authorization QR with TikTok on the phone',
|
|
17206
|
+
continuesPolling: true,
|
|
17207
|
+
materialUploadEnabled: true,
|
|
17208
|
+
phoneAuthorizationVerified: true,
|
|
17209
|
+
authorizationQrPayload: 'landing_url',
|
|
17210
|
+
pageMetadata: ['title', 'icon', 'description', 'category', 'subcategory'],
|
|
17211
|
+
},
|
|
17190
17212
|
skillWorkflow: {
|
|
17191
17213
|
preferredFormat: 'ndjson',
|
|
17192
17214
|
packageAlternative: '--package <zipPath> replaces --dir <gameDir>',
|
|
@@ -17195,7 +17217,7 @@ function getCapabilities() {
|
|
|
17195
17217
|
uploadOwner: 'cli',
|
|
17196
17218
|
waitingAuthorization: {
|
|
17197
17219
|
status: 'waiting_authorization',
|
|
17198
|
-
userAction: '
|
|
17220
|
+
userAction: 'Open pageUrl on the CLI computer and scan the QR with TikTok on the phone',
|
|
17199
17221
|
pageUrlField: 'pageUrl',
|
|
17200
17222
|
pageUrlPurpose: 'local binding and upload progress page',
|
|
17201
17223
|
reopenCommandField: 'authorizationOpenCommand',
|
|
@@ -17245,7 +17267,7 @@ function getCapabilities() {
|
|
|
17245
17267
|
previewQr: false,
|
|
17246
17268
|
realAuthorizationVerified: true,
|
|
17247
17269
|
realUploadVerified: true,
|
|
17248
|
-
verificationScope: { backend: 'ppe', assets: 'test-fixtures', date: '2026-09-
|
|
17270
|
+
verificationScope: { backend: 'ppe', authorizationMode: 'qr', assets: 'test-fixtures', date: '2026-09-16', production: false },
|
|
17249
17271
|
autoUploadAfterAuthorization: true,
|
|
17250
17272
|
boundedPolling: true,
|
|
17251
17273
|
cancellation: true,
|
|
@@ -17263,7 +17285,7 @@ function getCapabilities() {
|
|
|
17263
17285
|
requiresRunningUpload: true,
|
|
17264
17286
|
createsTask: false,
|
|
17265
17287
|
outputContainsLandingCredentials: false,
|
|
17266
|
-
target: '
|
|
17288
|
+
target: 'local-qr-page',
|
|
17267
17289
|
successStatus: 'open_requested',
|
|
17268
17290
|
provesAuthorization: false,
|
|
17269
17291
|
},
|
|
@@ -18001,7 +18023,7 @@ withVerboseOption(program
|
|
|
18001
18023
|
.option('--serve-preview-page', t('cli.command.upload.servePreviewPage'))
|
|
18002
18024
|
.option('--no-open', t('cli.command.upload.noOpen'))
|
|
18003
18025
|
.option('--vibe', t('cli.command.upload.vibe'))
|
|
18004
|
-
.
|
|
18026
|
+
.addOption(new commander.Option('--web', t('cli.command.upload.vibeWeb')).hideHelp())
|
|
18005
18027
|
// Vibe v2 backend selection (including opt-in ppe-mock) stays outside the Skill workflow.
|
|
18006
18028
|
.option('--title <title>', t('cli.command.upload.vibeTitle'))
|
|
18007
18029
|
.option('--description <description>', t('cli.command.upload.vibeDescription'))
|
|
@@ -18555,7 +18577,7 @@ async function executeVibeAuthOpen(options, deps = { open: openVibeBrowser }) {
|
|
|
18555
18577
|
const url = authorizationOpenUrl(options.pageUrl);
|
|
18556
18578
|
await checkAuthorizationPage(url);
|
|
18557
18579
|
try {
|
|
18558
|
-
await deps.open(
|
|
18580
|
+
await deps.open(options.pageUrl);
|
|
18559
18581
|
}
|
|
18560
18582
|
catch {
|
|
18561
18583
|
throw new VibeError('VIBE_BROWSER_OPEN_FAILED');
|
|
@@ -18970,8 +18992,9 @@ function createMockBackend(scenario = 'success') {
|
|
|
18970
18992
|
}
|
|
18971
18993
|
|
|
18972
18994
|
function createAuthorizationScheme(landingUrl) {
|
|
18973
|
-
//
|
|
18974
|
-
|
|
18995
|
+
// QR and browser authorization use the same backend-provided landing URL.
|
|
18996
|
+
// Preserve its query exactly; do not wrap it in an app scheme or re-encode it.
|
|
18997
|
+
return safeWebUrl(landingUrl);
|
|
18975
18998
|
}
|
|
18976
18999
|
|
|
18977
19000
|
const DEV_PORT = 3000;
|
|
@@ -19257,20 +19280,54 @@ async function prepareVibeInput(options, onPackaging, signal, deps = { check: ex
|
|
|
19257
19280
|
}
|
|
19258
19281
|
|
|
19259
19282
|
const vibePageStyles = `
|
|
19260
|
-
|
|
19261
|
-
:root{font-family:Inter,-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif;color:#
|
|
19262
|
-
*{box-sizing:border-box}
|
|
19263
|
-
|
|
19264
|
-
.
|
|
19265
|
-
.
|
|
19266
|
-
.
|
|
19267
|
-
.
|
|
19268
|
-
.
|
|
19269
|
-
|
|
19283
|
+
|
|
19284
|
+
:root{font-family:Inter,-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif;color:#24282f;background:#fff;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased}
|
|
19285
|
+
*{box-sizing:border-box}
|
|
19286
|
+
body{margin:0;min-height:100vh;min-height:100svh;display:flex;align-items:center;justify-content:center;padding:36px 24px;font-size:14px;line-height:1.5}
|
|
19287
|
+
.shell{width:100%;max-width:400px}
|
|
19288
|
+
.steps{display:flex;align-items:center;justify-content:center;gap:10px;margin:0 0 32px;padding:0;list-style:none}
|
|
19289
|
+
.step{display:flex;align-items:center;gap:6px;font-size:11px;color:#9298a2;white-space:nowrap}
|
|
19290
|
+
.step:after{content:"";height:1px;width:25px;margin-left:4px;background:#e9ebef}.step:last-child:after{display:none}
|
|
19291
|
+
.step-number{height:18px;min-width:18px;border-radius:50%;border:1px solid #e2e5e9;display:grid;place-items:center;font-size:10px}
|
|
19292
|
+
.step.active{color:#272c34;font-weight:600}.step.active .step-number{background:#272c34;color:#fff;border-color:#272c34}
|
|
19293
|
+
.step.done{color:#537565}.step.done .step-number{background:#edf6f0;color:#287c54;border-color:#edf6f0}
|
|
19294
|
+
.heading{text-align:center}h1{font-size:26px;font-weight:650;line-height:1.35;letter-spacing:-.6px;margin:0 0 10px}
|
|
19295
|
+
.subtitle{color:#7a818c;font-size:12px;line-height:1.7;margin:0;overflow-wrap:anywhere}
|
|
19296
|
+
.game{margin:28px 0 0;padding-top:24px;border-top:1px solid #eceef1}
|
|
19297
|
+
.game-summary{display:flex;align-items:center;gap:12px}.game-title{margin:0;font-size:15px;font-weight:600;overflow-wrap:anywhere;min-width:0}
|
|
19298
|
+
.game-icon,.game-avatar{width:44px;height:44px;border-radius:10px;flex-shrink:0;background:#f0f2f5}.game-avatar{object-fit:cover}.game-icon{display:grid;place-items:center;color:#707b8b}
|
|
19299
|
+
.game-details{margin:20px 0 0;font-size:13px}.game-details dt{font-size:11px;color:#9298a2;margin-bottom:4px}.game-details dd{margin:0;overflow-wrap:anywhere}
|
|
19300
|
+
.game-description{white-space:pre-wrap;line-height:1.65}.game-categories{display:grid;grid-template-columns:1fr 1fr;gap:24px;margin-top:18px}
|
|
19301
|
+
.scan-action{text-align:center;font-size:13px;font-weight:500;margin:26px 0 10px}.qr-area{display:flex;justify-content:center}.qr-frame{padding:8px;width:208px;height:208px}.qr-frame img{display:block;width:192px;height:192px;max-width:100%}
|
|
19302
|
+
.upload-area{display:flex;align-items:center;flex-direction:column;gap:16px;padding-top:26px}.percentage{font-size:34px;line-height:1;font-variant-numeric:tabular-nums;font-weight:550}.percentage span{font-size:16px;color:#858e9d;padding-left:3px}
|
|
19303
|
+
.progress-wrap{width:100%}progress{display:block;width:100%;height:4px;border:none;border-radius:20px;overflow:hidden;background:#edf0f4;accent-color:#4c6378}progress::-webkit-progress-bar{background:#edf0f4;border-radius:20px}progress::-webkit-progress-value{background:#4c6378;border-radius:20px}progress::-moz-progress-bar{background:#4c6378;border-radius:20px}.transfer-label{text-align:center;font-size:11px;color:#8b939f;margin:8px 0 0}
|
|
19304
|
+
.footer{text-align:center;font-size:10px;line-height:1.7;color:#a0a5ae;margin:28px 0 0}.footer .dot{display:inline-block;height:5px;width:5px;border-radius:50%;background:#c1995e;vertical-align:middle;margin-right:5px}.mock-badge{width:fit-content;margin:0 0 10px auto;font-size:10px;padding:2px 7px;border:1px solid #dfe2e7;border-radius:5px;color:#707783}
|
|
19305
|
+
@media(max-width:380px){body{padding:28px 20px}.steps{gap:7px}.step:after{width:16px;margin-left:1px}h1{font-size:24px}}
|
|
19306
|
+
@media(max-height:640px){body{align-items:flex-start;padding-top:28px;padding-bottom:28px}}
|
|
19307
|
+
`;
|
|
19270
19308
|
|
|
19271
19309
|
const VIBE_PAGE_WORKER = '__vibe-page-server';
|
|
19272
19310
|
// Includes the base64 form of the locally validated image (up to 10 MiB).
|
|
19273
19311
|
const VIBE_PAGE_HTML_LIMIT = 16 * 1024 * 1024;
|
|
19312
|
+
const pageUpdates = `(() => {
|
|
19313
|
+
let etag;
|
|
19314
|
+
const terminal = () => ['complete', 'failed'].includes(document.querySelector('main')?.dataset.phase);
|
|
19315
|
+
async function refresh() {
|
|
19316
|
+
if (terminal()) return;
|
|
19317
|
+
try {
|
|
19318
|
+
const response = await fetch(location.pathname, {cache: 'no-store', headers: etag ? {'If-None-Match': etag} : {}});
|
|
19319
|
+
if (response.ok) {
|
|
19320
|
+
etag = response.headers.get('etag');
|
|
19321
|
+
const next = new DOMParser().parseFromString(await response.text(), 'text/html');
|
|
19322
|
+
const current = document.querySelector('main'), updated = next.querySelector('main');
|
|
19323
|
+
if (current && updated && current.outerHTML !== updated.outerHTML) current.replaceWith(updated);
|
|
19324
|
+
document.title = next.title;
|
|
19325
|
+
}
|
|
19326
|
+
} catch { /* Keep the current page readable during a temporary connection failure. */ }
|
|
19327
|
+
if (!terminal()) setTimeout(refresh, 2000);
|
|
19328
|
+
}
|
|
19329
|
+
if (!terminal()) setTimeout(refresh, 2000);
|
|
19330
|
+
})();`;
|
|
19274
19331
|
const escape = (s) => s.replace(/[&<>"']/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c]);
|
|
19275
19332
|
async function renderVibePage(input) {
|
|
19276
19333
|
const zh = input.language !== 'en-US';
|
|
@@ -19280,7 +19337,6 @@ async function renderVibePage(input) {
|
|
|
19280
19337
|
? {
|
|
19281
19338
|
steps: ['扫码授权', '上传游戏', '完成'],
|
|
19282
19339
|
mock: input.hybrid ? 'PPE + Mock' : '模拟',
|
|
19283
|
-
game: '当前游戏',
|
|
19284
19340
|
gameIcon: '游戏头像',
|
|
19285
19341
|
bindingTitle: '上传到 TikTok',
|
|
19286
19342
|
bindingAction: input.hybrid ? '真实授权二维码 · 混合联调' : '使用 TikTok 扫描二维码',
|
|
@@ -19293,7 +19349,6 @@ async function renderVibePage(input) {
|
|
|
19293
19349
|
uploadingTitle: '正在上传小游戏',
|
|
19294
19350
|
uploadingSubtitle: input.simulation ? '正在模拟授权后的上传流程,不会上传游戏内容。' : '授权已完成,正在上传本次游戏内容。',
|
|
19295
19351
|
uploadLabel: input.simulation ? '模拟上传进度' : '上传进度',
|
|
19296
|
-
uploadStatus: '正在上传游戏,请稍候',
|
|
19297
19352
|
completeTitle: input.simulation ? '模拟上传完成' : '上传完成',
|
|
19298
19353
|
completeSubtitle: input.simulation
|
|
19299
19354
|
? '绑定和上传流程模拟完成,未向 TikTok 上传游戏。'
|
|
@@ -19307,7 +19362,6 @@ async function renderVibePage(input) {
|
|
|
19307
19362
|
: {
|
|
19308
19363
|
steps: ['Authorize', 'Upload', 'Done'],
|
|
19309
19364
|
mock: input.hybrid ? 'PPE + Mock' : 'MOCK',
|
|
19310
|
-
game: 'YOUR GAME',
|
|
19311
19365
|
gameIcon: 'Game icon',
|
|
19312
19366
|
bindingTitle: 'Upload to TikTok',
|
|
19313
19367
|
bindingAction: input.hybrid ? 'Real authorization QR · integration demo' : 'Scan the QR code with TikTok',
|
|
@@ -19324,7 +19378,6 @@ async function renderVibePage(input) {
|
|
|
19324
19378
|
uploadLabel: input.simulation
|
|
19325
19379
|
? 'Simulated upload progress'
|
|
19326
19380
|
: 'Upload progress',
|
|
19327
|
-
uploadStatus: 'Uploading your game. Please wait.',
|
|
19328
19381
|
completeTitle: input.simulation ? 'Simulated upload complete' : 'Upload complete',
|
|
19329
19382
|
completeSubtitle: input.simulation
|
|
19330
19383
|
? 'Binding and upload simulation complete. Nothing was uploaded to TikTok.'
|
|
@@ -19350,7 +19403,7 @@ async function renderVibePage(input) {
|
|
|
19350
19403
|
/^data:image\/(?:png|jpeg|webp);base64,[A-Za-z0-9+/]+=*$/.test(input.iconDataUrl)
|
|
19351
19404
|
? `<img class="game-avatar" width="44" height="44" alt="${copy.gameIcon}" src="${input.iconDataUrl}">`
|
|
19352
19405
|
: '<span class="game-icon" aria-hidden="true"><svg width="22" height="22" viewBox="0 0 24 24" fill="none"><path d="M7 7h10c2 0 3 2 3 4l1 5c.4 2-1 3-2.5 2l-3-2h-7l-3 2C4 19 2.6 18 3 16l1-5c0-2 1-4 3-4Z" stroke="currentColor" stroke-width="1.4" stroke-linejoin="round"/><path d="M8 10v4m-2-2h4m6-1h.01M18 13h.01" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></span>';
|
|
19353
|
-
const game = `<div class="game">${icon}<
|
|
19406
|
+
const game = `<div class="game"><div class="game-summary">${icon}<p class="game-title">${escape(input.title)}</p></div><dl class="game-details"><div><dt>${zh ? '描述' : 'Description'}</dt><dd class="game-description">${escape(input.description ?? VIBE_PPE_DEFAULT_APP_INFO.description)}</dd></div><div class="game-categories"><div><dt>${zh ? '类型' : 'Category'}</dt><dd>${escape(input.category ?? VIBE_PPE_DEFAULT_APP_INFO.category)}</dd></div><div><dt>${zh ? '子类型' : 'Subcategory'}</dt><dd>${escape(input.subcategory ?? VIBE_PPE_DEFAULT_APP_INFO.subcategory)}</dd></div></div></dl></div>`;
|
|
19354
19407
|
const checkIcon = '<svg width="12" height="12" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="m3 8 3 3 7-7" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>';
|
|
19355
19408
|
const title = phase === 'binding'
|
|
19356
19409
|
? copy.bindingTitle
|
|
@@ -19378,50 +19431,43 @@ async function renderVibePage(input) {
|
|
|
19378
19431
|
Number.isFinite(input.progressPercent)
|
|
19379
19432
|
? Math.max(0, Math.min(100, Math.round(input.progressPercent)))
|
|
19380
19433
|
: undefined;
|
|
19381
|
-
const
|
|
19382
|
-
?
|
|
19383
|
-
: phase === 'uploading'
|
|
19384
|
-
? copy.
|
|
19385
|
-
:
|
|
19386
|
-
|
|
19387
|
-
|
|
19388
|
-
|
|
19389
|
-
|
|
19390
|
-
: qr
|
|
19391
|
-
? `<div class="qr-area"><div class="qr-frame"><img width="192" height="192" alt="${purpose}" src="${qr}"></div></div>`
|
|
19392
|
-
: phase === 'uploading'
|
|
19393
|
-
? `<div class="upload-area"><div class="upload-symbol"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M12 16V4m-4 4 4-4 4 4M5 15v5h14v-5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg></div>${progress !== undefined ? `<div class="percentage">${progress}<span>%</span></div><div class="progress-wrap"><progress aria-label="${copy.uploadLabel}" max="100" value="${progress}"></progress><p class="transfer-label">${copy.uploadLabel}</p></div>` : '<span class="spinner" aria-hidden="true"></span>'}</div>`
|
|
19394
|
-
: `<div class="upload-area"><div class="pending-symbol">${phase === 'failed' ? '!' : checkIcon}</div></div>`;
|
|
19395
|
-
return `<!doctype html><html lang="${zh ? 'zh-CN' : 'en'}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta http-equiv="refresh" content="2"><title>${escape(title)} · ${escape(input.title)}</title><style>${vibePageStyles}</style></head>
|
|
19396
|
-
<body><main class="shell" data-phase="${phase}"><header class="brand"><span class="brand-mark" aria-hidden="true"><svg width="16" height="16" viewBox="0 0 20 20" fill="none"><path d="m7 5-4 5 4 5m6-10 4 5-4 5" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg></span><span class="brand-name">TikTok Minis</span><span class="brand-tool">DevTool</span>${input.simulation ? `<span class="mock-badge">${copy.mock}</span>` : ''}</header>
|
|
19397
|
-
<section class="card" aria-labelledby="page-title">${phase === 'failed' ? '' : `<ol class="steps" aria-label="${zh ? '上传步骤' : 'Upload steps'}">${copy.steps.map((label, i) => `<li class="step ${i < step ? 'done' : i === step ? 'active' : ''}"${i === step ? ' aria-current="step"' : ''}><span class="step-number">${i < step ? checkIcon : i + 1}</span>${label}</li>`).join('')}</ol>`}
|
|
19398
|
-
<div class="heading"><h1 id="page-title">${title}</h1></div>${game}<div class="heading">${phase === 'binding' ? `<p class="scan-action">${copy.bindingAction}</p>` : ''}<p class="subtitle">${subtitle}</p></div>${content}
|
|
19399
|
-
<p class="status${phase === 'complete' ? ' complete' : ''}" role="status">${phase === 'complete' ? `<span class="success-icon">${checkIcon}</span>` : phase === 'failed' ? '' : '<span class="spinner" aria-hidden="true"></span>'}${status}</p>
|
|
19434
|
+
const content = qr
|
|
19435
|
+
? `<div class="qr-area"><div class="qr-frame"><img width="192" height="192" alt="${purpose}" src="${qr}"></div></div>`
|
|
19436
|
+
: phase === 'uploading' && progress !== undefined
|
|
19437
|
+
? `<div class="upload-area"><div class="percentage">${progress}<span>%</span></div><div class="progress-wrap"><progress aria-label="${copy.uploadLabel}" max="100" value="${progress}"></progress><p class="transfer-label">${copy.uploadLabel}</p></div></div>`
|
|
19438
|
+
: '';
|
|
19439
|
+
return `<!doctype html><html lang="${zh ? 'zh-CN' : 'en'}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>${escape(title)} · ${escape(input.title)}</title><style>${vibePageStyles}</style><script src="?updates" defer></script></head>
|
|
19440
|
+
<body><main class="shell" data-phase="${phase}">${input.simulation ? `<p class="mock-badge">${copy.mock}</p>` : ''}
|
|
19441
|
+
<section class="card" aria-labelledby="page-title">${phase === 'failed' ? '' : `<ol class="steps" aria-label="${zh ? '上传步骤' : 'Upload steps'}">${copy.steps.map((label, i) => `<li class="step ${phase === 'complete' || i < step ? 'done' : i === step ? 'active' : ''}"${phase !== 'complete' && i === step ? ' aria-current="step"' : ''}><span class="step-number">${phase === 'complete' || i < step ? checkIcon : i + 1}</span>${label}</li>`).join('')}</ol>`}
|
|
19442
|
+
<div class="heading"><h1 id="page-title">${title}</h1><p class="subtitle" role="status">${subtitle}</p></div>${game}${phase === 'binding' ? `<p class="scan-action">${copy.bindingAction}</p>` : ''}${content}
|
|
19400
19443
|
</section>
|
|
19401
19444
|
<p class="footer">${input.simulation ? `<span class="dot" aria-hidden="true"></span>${copy.mockNote}` : copy.footer}</p></main></body></html>`;
|
|
19402
19445
|
}
|
|
19403
19446
|
async function createVibePageServer() {
|
|
19404
19447
|
const route = `/${crypto.randomBytes(24).toString('hex')}`;
|
|
19405
19448
|
let html = 'Starting…';
|
|
19449
|
+
let etag = '';
|
|
19406
19450
|
let landingUrl;
|
|
19407
19451
|
const server = http.createServer((req, res) => {
|
|
19408
19452
|
const address = server.address();
|
|
19409
19453
|
const host = address && typeof address !== 'string' ? `127.0.0.1:${address.port}` : '';
|
|
19454
|
+
if (req.method === 'GET' && req.url === `${route}?updates` && req.headers.host === host) {
|
|
19455
|
+
res.writeHead(200, { 'Content-Type': 'text/javascript; charset=utf-8', 'Cache-Control': 'no-store', 'X-Content-Type-Options': 'nosniff' });
|
|
19456
|
+
res.end(pageUpdates);
|
|
19457
|
+
return;
|
|
19458
|
+
}
|
|
19410
19459
|
if (req.url === `${route}/authorize` && req.headers.host === host &&
|
|
19411
19460
|
['GET', 'HEAD'].includes(req.method || '') &&
|
|
19412
19461
|
(!req.headers.origin || req.headers.origin === `http://${host}`) &&
|
|
19413
19462
|
req.headers['sec-fetch-site'] !== 'cross-site') {
|
|
19414
19463
|
const headers = { 'Cache-Control': 'no-store', 'Referrer-Policy': 'no-referrer' };
|
|
19415
|
-
if (!landingUrl) {
|
|
19464
|
+
if (!landingUrl || req.method === 'GET') {
|
|
19416
19465
|
res.writeHead(410, { ...headers, 'Content-Type': 'text/plain; charset=utf-8' });
|
|
19417
|
-
res.end('
|
|
19466
|
+
res.end('Web authorization is disabled. Open the local QR page and scan with TikTok.');
|
|
19418
19467
|
}
|
|
19419
19468
|
else if (req.method === 'HEAD') {
|
|
19420
19469
|
res.writeHead(204, { ...headers, 'X-TTMG-Authorization': 'ready' }).end();
|
|
19421
19470
|
}
|
|
19422
|
-
else {
|
|
19423
|
-
res.writeHead(302, { ...headers, Location: landingUrl }).end();
|
|
19424
|
-
}
|
|
19425
19471
|
return;
|
|
19426
19472
|
}
|
|
19427
19473
|
if (req.method !== 'GET' ||
|
|
@@ -19430,12 +19476,17 @@ async function createVibePageServer() {
|
|
|
19430
19476
|
res.writeHead(404).end();
|
|
19431
19477
|
return;
|
|
19432
19478
|
}
|
|
19479
|
+
if (etag && req.headers['if-none-match'] === etag) {
|
|
19480
|
+
res.writeHead(304, { 'ETag': etag, 'Cache-Control': 'no-store' }).end();
|
|
19481
|
+
return;
|
|
19482
|
+
}
|
|
19433
19483
|
res.writeHead(200, {
|
|
19484
|
+
'ETag': etag,
|
|
19434
19485
|
'Content-Type': 'text/html; charset=utf-8',
|
|
19435
19486
|
'Cache-Control': 'no-store',
|
|
19436
19487
|
'Referrer-Policy': 'no-referrer',
|
|
19437
19488
|
'X-Content-Type-Options': 'nosniff',
|
|
19438
|
-
'Content-Security-Policy': "default-src 'none'; img-src data:; style-src 'unsafe-inline'; frame-ancestors 'none'",
|
|
19489
|
+
'Content-Security-Policy': "default-src 'none'; img-src data:; style-src 'unsafe-inline'; script-src 'self'; connect-src 'self'; frame-ancestors 'none'",
|
|
19439
19490
|
});
|
|
19440
19491
|
res.end(html);
|
|
19441
19492
|
});
|
|
@@ -19448,6 +19499,7 @@ async function createVibePageServer() {
|
|
|
19448
19499
|
url: `http://127.0.0.1:${address.port}${route}`,
|
|
19449
19500
|
update: (value, authorization) => {
|
|
19450
19501
|
html = value;
|
|
19502
|
+
etag = '"' + crypto.createHash('sha256').update(value).digest('hex') + '"';
|
|
19451
19503
|
landingUrl = browserLandingUrl(authorization);
|
|
19452
19504
|
},
|
|
19453
19505
|
close: () => server.close(),
|
|
@@ -19654,6 +19706,7 @@ async function executeVibeUpload(options, onEvent = () => { }, dependencies = {}
|
|
|
19654
19706
|
let ppeLane;
|
|
19655
19707
|
let ppeSelected = false;
|
|
19656
19708
|
let hybridSelected = false;
|
|
19709
|
+
let pageMetadata;
|
|
19657
19710
|
let result = {
|
|
19658
19711
|
schemaVersion: '2',
|
|
19659
19712
|
command: 'upload',
|
|
@@ -19689,20 +19742,11 @@ async function executeVibeUpload(options, onEvent = () => { }, dependencies = {}
|
|
|
19689
19742
|
throw new VibeError('VIBE_CANCELLED');
|
|
19690
19743
|
};
|
|
19691
19744
|
try {
|
|
19692
|
-
|
|
19693
|
-
|
|
19694
|
-
//
|
|
19695
|
-
|
|
19696
|
-
|
|
19697
|
-
throw new VibeError('VIBE_WEB_OPTION_CONFLICT');
|
|
19698
|
-
ppeSelected = true;
|
|
19699
|
-
ppeLane = process.env.TTMG_VIBE_PPE || 'ppe_op_vc';
|
|
19700
|
-
if (!/^ppe_[a-zA-Z0-9_-]{1,64}$/.test(ppeLane))
|
|
19701
|
-
throw new VibeError('VIBE_INVALID_PPE_ENV');
|
|
19702
|
-
if (deps.backend && deps.backend.mode !== 'ppe')
|
|
19703
|
-
throw new VibeError('VIBE_WEB_OPTION_CONFLICT');
|
|
19704
|
-
}
|
|
19705
|
-
else if (!options.mock && !options.vibeApi && process.env.TTMG_VIBE_BACKEND) {
|
|
19745
|
+
if (options.web)
|
|
19746
|
+
throw new VibeError('VIBE_WEB_DISABLED');
|
|
19747
|
+
// Real QR uploads use the release-owned PPE route.
|
|
19748
|
+
// Explicit test backends remain opt-in; real failures never fall back to them.
|
|
19749
|
+
if (!options.mock && !options.vibeApi && process.env.TTMG_VIBE_BACKEND) {
|
|
19706
19750
|
const configured = process.env.TTMG_VIBE_BACKEND;
|
|
19707
19751
|
if (configured === 'mock')
|
|
19708
19752
|
options = { ...options, mock: true };
|
|
@@ -19720,6 +19764,12 @@ async function executeVibeUpload(options, onEvent = () => { }, dependencies = {}
|
|
|
19720
19764
|
else
|
|
19721
19765
|
throw new VibeError('VIBE_BACKEND_NOT_CONFIGURED');
|
|
19722
19766
|
}
|
|
19767
|
+
else if (!options.mock && !options.vibeApi) {
|
|
19768
|
+
ppeSelected = true;
|
|
19769
|
+
ppeLane = process.env.TTMG_VIBE_PPE || 'ppe_op_vc';
|
|
19770
|
+
if (!/^ppe_[a-zA-Z0-9_-]{1,64}$/.test(ppeLane))
|
|
19771
|
+
throw new VibeError('VIBE_INVALID_PPE_ENV');
|
|
19772
|
+
}
|
|
19723
19773
|
if (!['text', 'json', 'ndjson'].includes(options.format || 'text'))
|
|
19724
19774
|
throw new VibeError('VIBE_INVALID_FORMAT');
|
|
19725
19775
|
if (options.clientKey ||
|
|
@@ -19779,12 +19829,18 @@ async function executeVibeUpload(options, onEvent = () => { }, dependencies = {}
|
|
|
19779
19829
|
});
|
|
19780
19830
|
if (options.dryRun)
|
|
19781
19831
|
return result;
|
|
19832
|
+
const submittedInfo = input.appInfo || VIBE_PPE_DEFAULT_APP_INFO;
|
|
19833
|
+
pageMetadata = {
|
|
19834
|
+
iconDataUrl: `data:${input.icon.contentType};base64,${input.icon.bytes.toString('base64')}`,
|
|
19835
|
+
description: submittedInfo.description,
|
|
19836
|
+
category: submittedInfo.category,
|
|
19837
|
+
subcategory: submittedInfo.subcategory,
|
|
19838
|
+
};
|
|
19782
19839
|
const pageGame = {
|
|
19783
19840
|
title: input.title,
|
|
19784
|
-
|
|
19841
|
+
...pageMetadata,
|
|
19785
19842
|
uploadAvailable: backend.uploadAvailable !== false,
|
|
19786
19843
|
hybrid: backend.mode === 'ppe-mock',
|
|
19787
|
-
web: !!options.web,
|
|
19788
19844
|
};
|
|
19789
19845
|
const sourceProjectId = deps.projectId(options);
|
|
19790
19846
|
emit('creating_task', { platformWrite: backend.mode === 'ppe' || backend.mode === 'ppe-mock' });
|
|
@@ -19796,8 +19852,6 @@ async function executeVibeUpload(options, onEvent = () => { }, dependencies = {}
|
|
|
19796
19852
|
}, controller.signal);
|
|
19797
19853
|
ensureActive();
|
|
19798
19854
|
const authorizationUrl = createAuthorizationScheme(task.landingUrl);
|
|
19799
|
-
if (options.web && !browserLandingUrl(task.landingUrl))
|
|
19800
|
-
throw new VibeError('VIBE_INVALID_RESPONSE');
|
|
19801
19855
|
const pollMs = seconds(options.pollInterval, task.pollIntervalSeconds);
|
|
19802
19856
|
result = {
|
|
19803
19857
|
...result,
|
|
@@ -19806,33 +19860,30 @@ async function executeVibeUpload(options, onEvent = () => { }, dependencies = {}
|
|
|
19806
19860
|
};
|
|
19807
19861
|
page = await deps.startPage();
|
|
19808
19862
|
ensureActive();
|
|
19809
|
-
const bindingMessage =
|
|
19863
|
+
const bindingMessage = backend.mode === 'ppe-mock'
|
|
19810
19864
|
? (getCurrentLanguage() === 'zh-CN'
|
|
19811
|
-
? '
|
|
19812
|
-
: '
|
|
19813
|
-
: backend.
|
|
19814
|
-
? (getCurrentLanguage() === 'zh-CN'
|
|
19815
|
-
|
|
19816
|
-
: 'Real PPE task and authorization QR. After three polls, authorization and upload are simulated; phone authorization is not required.')
|
|
19817
|
-
: backend.uploadAvailable === false
|
|
19818
|
-
? (getCurrentLanguage() === 'zh-CN' ? '请扫码完成真实 PPE 授权;上传尚未开放。' : 'Scan to authorize in PPE. Upload is not enabled yet.')
|
|
19819
|
-
: text('waiting_authorization', result.simulation);
|
|
19865
|
+
? '真实 PPE 任务和授权二维码;查询三次后模拟授权、上传,无需完成手机授权。'
|
|
19866
|
+
: 'Real PPE task and authorization QR. After three polls, authorization and upload are simulated; phone authorization is not required.')
|
|
19867
|
+
: backend.uploadAvailable === false
|
|
19868
|
+
? (getCurrentLanguage() === 'zh-CN' ? '请扫码完成真实 PPE 授权;上传尚未开放。' : 'Scan to authorize in PPE. Upload is not enabled yet.')
|
|
19869
|
+
: text('waiting_authorization', result.simulation);
|
|
19820
19870
|
await page.update({
|
|
19821
19871
|
...pageGame,
|
|
19822
19872
|
language: getCurrentLanguage(),
|
|
19823
19873
|
phase: 'binding',
|
|
19824
19874
|
simulation: result.simulation,
|
|
19825
19875
|
message: bindingMessage,
|
|
19826
|
-
qrUrl:
|
|
19876
|
+
qrUrl: authorizationUrl,
|
|
19827
19877
|
browserLandingUrl: task.landingUrl,
|
|
19828
19878
|
purpose: 'authorization_binding',
|
|
19829
19879
|
});
|
|
19830
19880
|
ensureActive();
|
|
19831
19881
|
let browserOpenRequested = false;
|
|
19832
19882
|
let browserOpenErrorCode;
|
|
19833
|
-
|
|
19883
|
+
const opensRealPage = backend.mode === 'ppe' && options.open !== false;
|
|
19884
|
+
if (opensRealPage) {
|
|
19834
19885
|
try {
|
|
19835
|
-
await deps.open(
|
|
19886
|
+
await deps.open(page.url);
|
|
19836
19887
|
browserOpenRequested = true;
|
|
19837
19888
|
}
|
|
19838
19889
|
catch {
|
|
@@ -19842,19 +19893,19 @@ async function executeVibeUpload(options, onEvent = () => { }, dependencies = {}
|
|
|
19842
19893
|
}
|
|
19843
19894
|
emit('waiting_authorization', {
|
|
19844
19895
|
message: browserOpenErrorCode
|
|
19845
|
-
? (getCurrentLanguage() === 'zh-CN' ? '
|
|
19896
|
+
? (getCurrentLanguage() === 'zh-CN' ? '二维码页面未能打开,请在 CLI 所在电脑打开 pageUrl 并使用 TikTok 扫码;当前任务仍在等待授权。' : 'Could not open the QR page. Open pageUrl on the CLI computer and scan with TikTok; this task is still waiting for authorization.')
|
|
19846
19897
|
: bindingMessage,
|
|
19847
19898
|
pageUrl: page.url,
|
|
19848
|
-
authorizationMode:
|
|
19849
|
-
...(
|
|
19899
|
+
authorizationMode: 'qr',
|
|
19900
|
+
...(backend.mode === 'ppe' ? { browserOpenRequested, browserOpenErrorCode } : {}),
|
|
19850
19901
|
authorizationOpenCommand: browserLandingUrl(task.landingUrl)
|
|
19851
19902
|
? `ttmg auth open --page-url ${page.url} --format json` : undefined,
|
|
19852
|
-
qrPurpose:
|
|
19853
|
-
nextAction: result.simulation ? 'wait' :
|
|
19903
|
+
qrPurpose: 'authorization_binding',
|
|
19904
|
+
nextAction: result.simulation ? 'wait' : 'scan_qr',
|
|
19854
19905
|
readyForInput: false,
|
|
19855
19906
|
});
|
|
19856
19907
|
// Opening a browser is presentation only and never gates token polling.
|
|
19857
|
-
if (!
|
|
19908
|
+
if (!opensRealPage && backend.mode !== 'ppe' && (options.format || 'text') === 'text' && options.open !== false)
|
|
19858
19909
|
void deps.open(page.url).catch(() => { });
|
|
19859
19910
|
while (true) {
|
|
19860
19911
|
ensureActive();
|
|
@@ -19965,7 +20016,6 @@ async function executeVibeUpload(options, onEvent = () => { }, dependencies = {}
|
|
|
19965
20016
|
VIBE_CATEGORY_PAIR_REQUIRED: ['--category 和 --subcategory 必须同时提供;均不传时保留默认分类。使用 ttmg game categories --format json 查询。', 'Provide --category and --subcategory together, or omit both to keep the default. Query ttmg game categories --format json.'],
|
|
19966
20017
|
VIBE_INVALID_CATEGORY: ['类型或子类型无效,或两者不匹配。使用 ttmg game categories --format json 查询有效组合。', 'Invalid category/subcategory pair. Query valid values with ttmg game categories --format json.'],
|
|
19967
20018
|
VIBE_INVALID_DESCRIPTION: ['描述必须是非空的有效 Unicode 文本;省略 --description 可使用默认描述。', 'Description must be non-empty valid Unicode text; omit --description to use the default.'],
|
|
19968
|
-
VIBE_WEB_OPTION_CONFLICT: ['--web 使用真实网页授权,不能与 --mock、--mock-scenario、--vibe-api 或 --no-open 同时使用。', '--web uses real browser authorization and cannot be combined with --mock, --mock-scenario, --vibe-api or --no-open.'],
|
|
19969
20019
|
VIBE_AUTH_DENIED: [
|
|
19970
20020
|
'已取消授权,本次未上传。',
|
|
19971
20021
|
'Authorization cancelled. No upload was performed.',
|
|
@@ -19997,6 +20047,10 @@ async function executeVibeUpload(options, onEvent = () => { }, dependencies = {}
|
|
|
19997
20047
|
'请检查本地 ZIP 代码包。',
|
|
19998
20048
|
'Check the local ZIP package.',
|
|
19999
20049
|
],
|
|
20050
|
+
VIBE_WEB_DISABLED: [
|
|
20051
|
+
'网页授权已关闭。请移除 --web,使用 ttmg upload --vibe 并在手机 TikTok 中扫码授权。',
|
|
20052
|
+
'Web authorization is disabled. Remove --web and use ttmg upload --vibe to scan with TikTok on your phone.',
|
|
20053
|
+
],
|
|
20000
20054
|
VIBE_BACKEND_NOT_CONFIGURED: [
|
|
20001
20055
|
'尚未配置服务;本地模拟请使用 --mock。',
|
|
20002
20056
|
'No backend configured. Use --mock for local simulation.',
|
|
@@ -20046,6 +20100,7 @@ async function executeVibeUpload(options, onEvent = () => { }, dependencies = {}
|
|
|
20046
20100
|
if (page && code !== 'VIBE_CANCELLED') {
|
|
20047
20101
|
try {
|
|
20048
20102
|
await page.update({
|
|
20103
|
+
...pageMetadata,
|
|
20049
20104
|
title: result.title || '',
|
|
20050
20105
|
language: getCurrentLanguage(),
|
|
20051
20106
|
simulation: result.simulation,
|