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

@@ -2,16 +2,15 @@ import {
copyFile,
mkdir,
readFile,
readdir,
rm,
unlink,
} from 'node:fs/promises';
import path from 'node:path';
import type { ProductModelRef } from '../coding-runtime/contracts';
import {
buildProjectAgentManifest,
normalizeProjectConfig,
} from '../opencode/project-config';
buildLegacyProjectAgentManifest,
normalizeLegacyProjectConfigV1,
} from './legacy-v1';
import type { ProjectAgentConfig, ProjectConfig } from '../../shared/project-config';
import {
atomicWriteJson,
@@ -90,32 +89,24 @@ async function snapshotLegacyAgentFiles(
copy: typeof copyFile,
): Promise<AgentFileSnapshot[]> {
const agentDirectory = path.join(projectPath, '.opencode', 'agent');
let entries;
try {
entries = await readdir(agentDirectory, { withFileTypes: true });
} catch (error) {
if (isMissing(error)) return [];
throw error;
}
const expected = new Map(buildProjectAgentManifest(config).entries.map((entry) => [
const expected = new Map(buildLegacyProjectAgentManifest(config).map((entry) => [
path.basename(entry.relativePath),
entry.content,
]));
const snapshots: AgentFileSnapshot[] = [];
for (const entry of entries) {
if (!entry.isFile()) continue;
const filePath = path.join(agentDirectory, entry.name);
const content = await readFile(filePath, 'utf8');
const generated = expected.get(entry.name) === content;
for (const [fileName, expectedContent] of expected) {
const filePath = path.join(agentDirectory, fileName);
const content = await readOptionalText(filePath);
if (content === null) continue;
const generated = expectedContent === content;
if (generated) {
snapshots.push({ fileName: entry.name, filePath, content, generated });
snapshots.push({ fileName, filePath, content, generated });
continue;
}
const backupPath = path.join(backupDirectory, '.opencode', 'agent', entry.name);
const backupPath = path.join(backupDirectory, '.opencode', 'agent', fileName);
await mkdir(path.dirname(backupPath), { recursive: true });
await copy(filePath, backupPath);
snapshots.push({ fileName: entry.name, filePath, content, generated, backupPath });
snapshots.push({ fileName, filePath, content, generated, backupPath });
}
return snapshots;
}
@@ -239,7 +230,7 @@ export async function migrateCodingProjectToV2(
config: normalizeCodingProjectConfigV2(rawProject),
};
}
const normalizedLegacyConfig = normalizeProjectConfig(rawProject);
const normalizedLegacyConfig = normalizeLegacyProjectConfigV1(rawProject);
const legacyConfig = preserveLegacyAgentFields(rawProject, normalizedLegacyConfig);
const conversationsSource = await readOptionalText(conversationsPath);
const migrationTimestamp = dependencies.now?.() ?? new Date().toISOString();
@@ -247,7 +238,7 @@ export async function migrateCodingProjectToV2(
projectPath,
'.niancode',
'migration-backups',
`opencode-cutover-${timestampKey(migrationTimestamp)}`,
`pi-cutover-${timestampKey(migrationTimestamp)}`,
);
const copy = dependencies.copyFile ?? copyFile;
const writeJson = dependencies.writeJson ?? atomicWriteJson;