import { expect, test } from '@playwright/test'; import { resolve } from 'node:path'; import { readFile, writeFile } from 'node:fs/promises'; import { subscriptionOperation } from './fixtures/subscriptionSession'; test.use({ trace: 'off', video: 'off' }); test('显式 opt-in:已人工登录的 ChatGPT 订阅 + Jev 主视口物理验收与退出', async ({ page, }, info) => { test.skip( process.env.LEKIWI_LIVE_SUBSCRIPTION !== '1', '需要用户在隔离官方页面完成登录;不自动发起登录或使用全局账号', ); const session = await subscriptionOperation('connect'); const token = String(session.token); try { expect(session.llmProtocol).toBe('codex'); const before = await subscriptionOperation('status'); expect(before.loggedIn).toBe(true); const limitsBefore = await subscriptionOperation('limits'); await page.goto('/'); await page .locator('input[type="file"]') .first() .setInputFiles(resolve('build/lekiwi/lekiwi-v1.zip')); const dialog = page.getByRole('dialog', { name: '配置 URDF 仿真组件' }); await expect(dialog).toBeVisible({ timeout: 30000 }); await dialog.getByLabel('URDF 机器人 profile').selectOption('lekiwi-v1'); await dialog.getByRole('button', { name: '转换并加载' }).click(); await expect(page.getByRole('button', { name: '▶ 播放' })).toBeEnabled({ timeout: 60000 }); await page.getByRole('tab', { name: '控制台', exact: true }).click(); await page.getByRole('button', { name: 'LeKiwi 智能抓放', exact: true }).click(); const panel = page.getByLabel('LeKiwi 智能抓放任务', { exact: true }); await panel.getByText('模型连接与官方订阅登录', { exact: true }).click(); await panel.getByLabel('本机模型服务', { exact: true }).fill(String(session.address)); await panel.getByLabel('本机服务令牌', { exact: true }).fill(token); await panel.getByRole('button', { name: '连接本机模型服务', exact: true }).click(); await expect(panel).toContainText('本机桥已连接'); await expect(panel.getByLabel('LLM 协议', { exact: true })).toHaveValue('codex'); await expect(panel.getByLabel('LLM API Key', { exact: true })).toHaveCount(0); await panel.getByText(/ChatGPT 订阅 · 官方/).click(); await panel.getByRole('button', { name: '刷新账号', exact: true }).click(); await expect(panel).toContainText('ChatGPT 已登录(仅本次服务会话)'); await panel.getByRole('button', { name: '查询官方型号', exact: true }).click(); await expect(panel.getByLabel('LLM 型号', { exact: true })).toHaveValue( String(session.llmModel), ); await expect(panel.getByLabel('JEV 型号', { exact: true })).toHaveValue('typesafe/jev-1.13'); await panel.getByText('模型连接与官方订阅登录', { exact: true }).click(); await panel.getByLabel('智能任务决策模式').selectOption('api'); await panel.getByRole('button', { name: '开始任务(真实 API)', exact: true }).click(); await expect(panel.getByTestId('agent-task-state')).toContainText( /物理验收成功|失败 \/ 安全停止|已取消/, { timeout: 240000 }, ); const download = page.waitForEvent('download'); await panel.getByRole('button', { name: '导出任务证据', exact: true }).click(); const data = JSON.parse(await readFile((await (await download).path())!, 'utf8')); const after = await subscriptionOperation('status'); const evidence = { source: 'real-chatgpt-subscription-plus-openrouter-jev', before, after, limitsBefore, ...data, }; const encoded = JSON.stringify(evidence, null, 2); if (encoded.includes(token) || encoded.includes('auth.openai.com/')) throw new Error('任务证据包含认证信息'); await writeFile(info.outputPath('subscription-physical-evidence.json'), encoded); console.log('真实订阅回合', { state: data.state, error: data.error, model: session.llmModel, llm: data.llmCalls, jev: data.jevCalls, simTime: data.sample.time, transported: data.transported, }); expect(data.state, data.error).toBe('succeeded'); expect(data.succeeded).toBe(true); expect(data.transported).toBeGreaterThanOrEqual(0.5); expect(data.llmCalls).toBe(1); expect(data.jevCalls).toBe(11); expect(data.receipts).toHaveLength(12); expect(data.receipts.find((r: { kind: string }) => r.kind === 'plan').provider).toBe('codex'); expect(after.checkedModels).toContain(session.llmModel); await page.screenshot({ path: info.outputPath('subscription-workbench.png') }); await panel.getByText('模型连接与官方订阅登录', { exact: true }).click(); await panel.getByRole('button', { name: '退出隔离账号', exact: true }).click(); await expect(panel).toContainText('已退出本次隔离会话'); const loggedOut = await subscriptionOperation('status'); expect(loggedOut.loggedIn).toBe(false); await writeFile( info.outputPath('subscription-logout.json'), JSON.stringify(loggedOut, null, 2), ); } catch (error) { await page.close().catch(() => undefined); // eslint-disable-next-line preserve-caught-error -- Playwright causes can contain the private bridge token. throw new Error(String(error).replaceAll(token, '[redacted]')); } });