feat: remove legacy OpenCode runtime

Cut product flows over to Coding/Pi and retain only the migration-owned v1 boundary. Promote supported native optional packages because electron-builder omitted pnpm transitive optional closure from the packaged ASAR.
This commit is contained in:
2026-08-24 12:17:43 +08:00
parent 61fede2b4d
commit 5a275b93a7
199 changed files with 1330 additions and 64725 deletions

View File

@@ -60,6 +60,7 @@ async function installCodingFirstChatHost(
const { ipcMain } = process.mainModule!.require('electron') as typeof import('electron');
type MainState = {
captured: CapturedRequest[];
conversationCreated: boolean;
releaseSnapshot: (() => void) | null;
snapshotPending: boolean;
};
@@ -68,6 +69,7 @@ async function installCodingFirstChatHost(
};
const state: MainState = {
captured: [],
conversationCreated: false,
releaseSnapshot: null,
snapshotPending: false,
};
@@ -80,7 +82,11 @@ async function installCodingFirstChatHost(
updatedAt: now,
lastOpenedAt: now,
};
const legacyProject = { ...project, path: 'D:/e2e/pi-first-chat' };
const configuredModel = {
accountId: 'account-e2e',
modelId: 'model-a',
thinkingLevel: 'off',
};
const agent = {
id: 'builder',
avatarId: 'avatar-01',
@@ -88,8 +94,8 @@ async function installCodingFirstChatHost(
name: 'Builder',
builtIn: false,
enabled: true,
model: null,
modelResolution: 'required',
model: configuredModel,
modelResolution: 'resolved',
skillIds: [],
responsibility: {
mission: 'Implement',
@@ -114,22 +120,12 @@ async function installCodingFirstChatHost(
createdAt: now,
updatedAt: now,
};
const legacyConfig = {
schemaVersion: 1,
projectType: 'custom',
initialized: true,
defaultModel: null,
agents: [{ ...agent, model: null }],
knowledgeDirectory: 'knowledge',
createdAt: now,
updatedAt: now,
};
const conversation = {
id: 'conversation-pi-first-chat',
agentId: agent.id,
title: '新对话',
model: null,
modelResolution: 'required',
model: configuredModel,
modelResolution: 'resolved',
archivedAt: null,
unread: false,
createdAt: now,
@@ -152,7 +148,7 @@ async function installCodingFirstChatHost(
projectId: project.id,
agentId: agent.id,
title: conversation.title,
model: { model: null, modelResolution: 'required' },
model: { model: configuredModel, modelResolution: 'resolved' },
},
nodes: featureComplete ? [{
kind: 'subagent',
@@ -279,7 +275,7 @@ async function installCodingFirstChatHost(
return respond({ projects: [project], activeProjectId: project.id });
}
if (path === '/api/provider-accounts') {
return respond(featureComplete ? [{
return respond([{
id: 'account-e2e',
vendorId: 'custom',
label: 'E2E account',
@@ -290,18 +286,25 @@ async function installCodingFirstChatHost(
isDefault: true,
createdAt: now,
updatedAt: now,
}] : []);
}]);
}
if (path === '/api/provider-accounts/key-info') return respond([]);
if (path === '/api/provider-vendors') return respond(featureComplete ? [{ id: 'custom', name: 'Custom' }] : []);
if (path === '/api/provider-accounts/default') return respond({ accountId: featureComplete ? 'account-e2e' : null });
if (path === '/api/provider-vendors') return respond([{ id: 'custom', name: 'Custom' }]);
if (path === '/api/provider-accounts/default') return respond({ accountId: 'account-e2e' });
if (path === `/api/coding/projects/config?projectId=${project.id}`) {
return respond({ snapshot: { project, config, knowledgeFiles: [] } });
}
if (path === `/api/coding/projects/conversations?projectId=${project.id}`) {
return respond({ conversations: featureComplete ? [conversation, secondConversation] : [] });
return respond({
conversations: featureComplete
? [conversation, secondConversation]
: state.conversationCreated
? [conversation]
: [],
});
}
if (path === '/api/coding/projects/conversations' && method === 'POST') {
state.conversationCreated = true;
return respond({ conversation }, 201);
}
if (path === `/api/coding/conversations/${conversation.id}/snapshot`) {
@@ -353,23 +356,6 @@ async function installCodingFirstChatHost(
if (path.startsWith('/api/coding/skills')) return respond({ skills: [{ id: 'research', name: 'Research', description: 'Inspect sources', selected: true }] });
if (/^\/api\/coding\/conversations\/[^/]+\/commands$/.test(path)) return respond({ commands: [{ name: 'review', title: 'Review', description: 'Review changes', source: 'makelore' }] });
if (path === '/api/coding/runtime/diagnostics') return respond({ runtime: { revision: { provider: 1, resources: 1 }, workers: [{ conversationId: conversation.id, generation: 1, state: 'running', stage: 'running' }] } });
if (path.startsWith('/api/opencode/status')) {
return respond({ state: 'stopped', port: null, url: null });
}
if (path === '/api/opencode/projects' || path.startsWith('/api/opencode/projects?')) {
return respond({ projects: [legacyProject], activeProject: legacyProject });
}
if (path === '/api/opencode/projects/active') {
return respond({ projects: [legacyProject], activeProject: legacyProject });
}
if (path.startsWith('/api/opencode/projects/config?')) {
return respond({ status: 'valid', config: legacyConfig, knowledgeFiles: [] });
}
if (path.startsWith('/api/opencode/projects/conversations?')) {
return respond({
state: { schemaVersion: 1, sessions: [], updatedAt: now },
});
}
return respond({ success: false, error: `Unhandled E2E route: ${method} ${path}` }, 404);
});
}, { connection: hostConnection, featureComplete });
@@ -422,7 +408,7 @@ test('first PI Conversation is editable under 500 ms and submits before runtime
await expect(page.getByTestId('main-layout')).toBeVisible();
await page.evaluate(() => {
performance.mark('pi-first-chat-start');
window.location.hash = '/opencode-chat';
window.location.hash = '/chat';
});
const composer = page.getByRole('textbox');
await expect(composer).toBeEnabled();
@@ -503,7 +489,7 @@ test('PI feature UI isolates Conversations and exposes queue, interaction, model
page = await getStableWindow(electronApp);
await page.getByTestId('ai-module-option-programming').click();
await expect(page.getByTestId('main-layout')).toBeVisible();
await page.evaluate(() => { window.location.hash = '/opencode-chat'; });
await page.evaluate(() => { window.location.hash = '/chat'; });
await expect(page.getByTestId('coding-conversation-header')).toContainText('生成中');
await expect(page.getByTestId('coding-conversation-header')).toContainText('队列 1');