fix(pi): resolve packaged proxy token lazily

This commit is contained in:
2026-08-24 21:41:29 +08:00
parent fa510c4976
commit f902edefd0
13 changed files with 934 additions and 30 deletions

View File

@@ -129,6 +129,51 @@ function assertManagedTurns(extension) {
}
}
function assertProxyStatus(status) {
if (status?.currentHostTokenAvailable !== true
|| status?.realTurnVerified !== false
|| status?.activeProviderRequests?.child !== 1
|| status?.providerRequests?.parent < 1
|| status?.providerRequests?.child !== 1
|| status?.processes?.supported !== true
|| status?.processes?.parent?.length !== 1
|| status?.processes?.child?.length !== 1
|| Object.values(status?.tokenSafety ?? {}).some((safe) => safe !== true)) {
throw new Error(`Packaged Main proxy status is incomplete: ${JSON.stringify(status)}`);
}
}
function assertProxyProof(proof) {
if (proof?.providerMode !== 'loopback-through-authenticated-host-proxy'
|| proof?.realTurnVerified !== false
|| proof?.firstConversation?.count !== 1
|| proof?.firstConversation?.bindingEstablished !== true
|| proof?.firstConversation?.workerStatus !== 'ready'
|| proof?.firstConversation?.runStatus !== 'idle'
|| proof?.firstConversation?.userInputProjected !== true
|| proof?.firstConversation?.parentResponseProjected !== true
|| proof?.currentHostTokenUsedBy?.join(',') !== 'parent,child'
|| proof?.released?.workers !== 0
|| Object.values(proof?.tokenSafety ?? {}).some((safe) => safe !== true)) {
throw new Error(`Packaged Main proxy proof failed: ${JSON.stringify(proof)}`);
}
}
async function waitForActiveProxyProof(electronApplication) {
const deadline = Date.now() + 30_000;
let latest;
while (Date.now() < deadline) {
latest = await evaluateProof(electronApplication, 'proxy.status');
if (latest?.proxy?.activeProviderRequests?.child === 1
&& latest?.proxy?.processes?.parent?.length === 1
&& latest?.proxy?.processes?.child?.length === 1) {
return latest;
}
await new Promise((resolveWait) => setTimeout(resolveWait, 200));
}
throw new Error(`Packaged Main proxy child did not become active: ${JSON.stringify(latest?.proxy)}`);
}
async function evaluateProof(electronApplication, action) {
return await electronApplication.evaluate(async (_electron, requestedAction) => {
const proof = globalThis.__niancodeRunPiReleaseProofE2E;
@@ -157,6 +202,7 @@ export async function runPackagedProductProof(options) {
const hostApiPort = await allocatePort();
let electronApplication;
let pressureActive = false;
let proxyActive = false;
try {
electronApplication = await electron.launch({
executablePath: options.electronExecutable,
@@ -234,6 +280,53 @@ export async function runPackagedProductProof(options) {
};
}
const proxyStart = await evaluateProof(electronApplication, 'proxy.start');
proxyActive = true;
assertPackagedMain(proxyStart);
if (proxyStart?.proxy?.providerMode !== 'works_square_ai_gateway_proxy'
|| proxyStart?.proxy?.currentHostTokenAvailable !== true
|| proxyStart?.proxy?.realTurnVerified !== false) {
throw new Error(`Packaged Main proxy setup failed: ${JSON.stringify(proxyStart?.proxy)}`);
}
await page.reload();
await page.waitForLoadState('domcontentloaded');
const moduleSelection = page.getByTestId('ai-module-selection-page');
if (await moduleSelection.count()) {
await page.getByTestId('ai-module-option-programming').click();
await page.getByTestId('main-layout').waitFor({ state: 'visible', timeout: 10_000 });
}
await page.evaluate(() => { window.location.hash = '/chat'; });
const composer = page.getByRole('textbox');
await composer.waitFor({ state: 'visible', timeout: 30_000 });
await composer.fill('Exercise the packaged Main proxy Conversation');
if (!await composer.isEnabled()) throw new Error('Packaged proxy first Conversation composer is disabled');
const beforeSubmitText = await page.locator('body').innerText();
if (beforeSubmitText.includes('本地编程运行时暂时不可用')
|| beforeSubmitText.includes('正在重新连接本地 Agent')) {
throw new Error(`Packaged proxy first Conversation exposed a runtime banner: ${beforeSubmitText}`);
}
await page.getByTestId('coding-message-composer').evaluate(
(form) => form.requestSubmit(),
);
const proxyStatus = await waitForActiveProxyProof(electronApplication);
assertPackagedMain(proxyStatus);
assertProxyStatus(proxyStatus.proxy);
await evaluateProof(electronApplication, 'proxy.release-child');
await page.getByText('REAL_PARENT_COMPLETE').waitFor({ state: 'visible', timeout: 30_000 });
const afterTurnText = await page.locator('body').innerText();
if (afterTurnText.includes('本地编程运行时暂时不可用')
|| afterTurnText.includes('正在重新连接本地 Agent')) {
throw new Error(`Packaged proxy Conversation did not leave recovery cleanly: ${afterTurnText}`);
}
if (!await composer.isEnabled()) throw new Error('Packaged proxy composer is not editable after the turn');
const proxyFinish = await evaluateProof(electronApplication, 'proxy.finish');
proxyActive = false;
assertPackagedMain(proxyFinish);
assertProxyProof(proxyFinish.proxy);
const report = {
schemaVersion: 1,
generatedAt: new Date().toISOString(),
@@ -252,6 +345,18 @@ export async function runPackagedProductProof(options) {
released: pressureFinish.pressure,
...(failureCleanup ? { failureCleanup } : {}),
},
proxy: {
setup: proxyStart.proxy,
active: proxyStatus.proxy,
completed: proxyFinish.proxy,
ui: {
firstConversationEditable: true,
inputSubmitted: true,
runtimeUnavailableBanner: false,
permanentRecovering: false,
},
realTurnVerified: false,
},
result: 'pass',
};
if (options.reportPath) {
@@ -263,6 +368,10 @@ export async function runPackagedProductProof(options) {
if (electronApplication && pressureActive) {
await evaluateProof(electronApplication, 'pressure.finish').catch(() => undefined);
}
if (electronApplication && proxyActive) {
await evaluateProof(electronApplication, 'proxy.release-child').catch(() => undefined);
await evaluateProof(electronApplication, 'proxy.finish').catch(() => undefined);
}
if (electronApplication) await closeApplication(electronApplication);
await rm(scratchRoot, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 });
}