mirror of
https://github.com/microsoft/vscode.git
synced 2026-06-29 02:45:58 +01:00
177 lines
7.1 KiB
JavaScript
177 lines
7.1 KiB
JavaScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
// Regenerate vendored codex app-server protocol types.
|
|
//
|
|
// Resolution order for the codex binary:
|
|
// 1. $CODEX_BIN
|
|
// 2. node_modules/@openai/codex-<platform>-<arch>/vendor/.../bin/codex
|
|
// 3. `which codex`
|
|
//
|
|
// The locally installed binary's --version must match build/codex/codex-version.txt
|
|
// unless --no-version-check is passed.
|
|
//
|
|
// Generated files land in src/vs/platform/agentHost/node/codex/protocol/generated/
|
|
|
|
import { spawnSync } from 'node:child_process';
|
|
import { mkdtempSync, readdirSync, readFileSync, writeFileSync, statSync, rmSync, mkdirSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
const VERSION_FILE = path.join(REPO_ROOT, 'build', 'codex', 'codex-version.txt');
|
|
const OUT_DIR = path.join(REPO_ROOT, 'src', 'vs', 'platform', 'agentHost', 'node', 'codex', 'protocol', 'generated');
|
|
const FORMATTER = path.join(REPO_ROOT, 'build', 'lib', 'formatter.ts');
|
|
|
|
const args = process.argv.slice(2);
|
|
const noVersionCheck = args.includes('--no-version-check');
|
|
|
|
function resolveCodexBinary() {
|
|
if (process.env.CODEX_BIN) {
|
|
return process.env.CODEX_BIN;
|
|
}
|
|
const platformMap = {
|
|
'linux-x64': '@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl/bin/codex',
|
|
'linux-arm64': '@openai/codex-linux-arm64/vendor/aarch64-unknown-linux-musl/bin/codex',
|
|
'darwin-x64': '@openai/codex-darwin-x64/vendor/x86_64-apple-darwin/bin/codex',
|
|
'darwin-arm64': '@openai/codex-darwin-arm64/vendor/aarch64-apple-darwin/bin/codex',
|
|
'win32-x64': '@openai/codex-win32-x64/vendor/x86_64-pc-windows-msvc/bin/codex.exe',
|
|
'win32-arm64': '@openai/codex-win32-arm64/vendor/aarch64-pc-windows-msvc/bin/codex.exe',
|
|
};
|
|
const key = `${process.platform}-${process.arch}`;
|
|
const rel = platformMap[key];
|
|
if (rel) {
|
|
const candidate = path.join(REPO_ROOT, 'node_modules', rel);
|
|
try {
|
|
statSync(candidate);
|
|
return candidate;
|
|
} catch { /* fall through */ }
|
|
}
|
|
const which = spawnSync(process.platform === 'win32' ? 'where' : 'which', ['codex'], { encoding: 'utf8' });
|
|
if (which.status === 0) {
|
|
return which.stdout.trim().split(/\r?\n/)[0];
|
|
}
|
|
throw new Error('Cannot locate codex binary. Install via `npm install` (vendors @openai/codex) or set $CODEX_BIN.');
|
|
}
|
|
|
|
function readBinaryVersion(bin) {
|
|
const r = spawnSync(bin, ['--version'], { encoding: 'utf8' });
|
|
if (r.status !== 0) {
|
|
throw new Error(`codex --version failed:\n${r.stderr}`);
|
|
}
|
|
// Output looks like: "codex-cli 0.134.0"
|
|
const m = r.stdout.match(/(\d+\.\d+\.\d+(?:-[A-Za-z0-9.]+)?)/);
|
|
if (!m) {
|
|
throw new Error(`Could not parse codex version from: ${r.stdout}`);
|
|
}
|
|
return m[1];
|
|
}
|
|
|
|
function readPinnedVersion() {
|
|
return readFileSync(VERSION_FILE, 'utf8').trim();
|
|
}
|
|
|
|
function formatGeneratedTypes(files) {
|
|
for (let i = 0; i < files.length; i += 100) {
|
|
const batch = files.slice(i, i + 100);
|
|
const r = spawnSync(process.execPath, ['--experimental-strip-types', FORMATTER, '--replace', ...batch], { cwd: REPO_ROOT, encoding: 'utf8' });
|
|
if (r.status !== 0) {
|
|
throw new Error(`formatting generated protocol files failed:\n${r.stderr || r.stdout}`);
|
|
}
|
|
for (const file of batch) {
|
|
const formatted = readFileSync(file, 'utf8').replace(/\r\n/g, '\n');
|
|
writeFileSync(file, formatted);
|
|
}
|
|
}
|
|
}
|
|
|
|
function generate(bin, outDir, codexVersion) {
|
|
const tmp = mkdtempSync(path.join(tmpdir(), 'codex-gen-'));
|
|
const r = spawnSync(bin, ['app-server', 'generate-ts', '--out', tmp, '--experimental'], { encoding: 'utf8' });
|
|
if (r.status !== 0) {
|
|
throw new Error(`codex app-server generate-ts failed:\n${r.stderr}`);
|
|
}
|
|
// Wipe out existing generated content (excluding README.md).
|
|
for (const entry of readdirSync(outDir, { withFileTypes: true })) {
|
|
if (entry.name === 'README.md') { continue; }
|
|
rmSync(path.join(outDir, entry.name), { recursive: true, force: true });
|
|
}
|
|
const generatedFiles = [];
|
|
const HEADER = sourcePath =>
|
|
`// AUTOGENERATED - do not edit.\n` +
|
|
`// Source: \`codex app-server generate-ts --experimental\`\n` +
|
|
`// Generated from @openai/codex ${codexVersion}, licensed Apache-2.0.\n` +
|
|
`// Modified by VS Code to rewrite imports and apply repository formatting.\n` +
|
|
`// See README.md in this directory for provenance and licensing notes.\n` +
|
|
`// Regenerate: npm run codex:gen-protocol\n` +
|
|
`// Source file: ${sourcePath}\n\n`;
|
|
|
|
function copyRecursive(srcDir, dstDir, rel = '') {
|
|
for (const entry of readdirSync(srcDir, { withFileTypes: true })) {
|
|
const src = path.join(srcDir, entry.name);
|
|
const dst = path.join(dstDir, entry.name);
|
|
const relPath = path.posix.join(rel, entry.name);
|
|
if (entry.isDirectory()) {
|
|
mkdirSync(dst, { recursive: true });
|
|
copyRecursive(src, dst, relPath);
|
|
} else if (entry.isFile() && entry.name.endsWith('.ts')) {
|
|
const body = readFileSync(src, 'utf8')
|
|
// Strip ts-rs's own header so we don't double up
|
|
.replace(/^\/\/ GENERATED CODE!.*$\n/m, '')
|
|
.replace(/^\/\/ This file was generated by.*$\n/m, '')
|
|
.replace(/^\s*\n/, '')
|
|
// Rewrite relative imports to use .js extension (VS Code uses node16 module resolution).
|
|
// Directory specifiers (no trailing file part) become `<dir>/index.js`.
|
|
.replace(/(from\s+["'])(\.\.?\/[^"']+?)(["'])/g, (_m, p, spec, q) => {
|
|
// If the spec resolves to a directory in our generated tree, point at its index.
|
|
const abs = path.resolve(path.dirname(src), spec);
|
|
let suffix = '.js';
|
|
try {
|
|
if (statSync(abs).isDirectory()) {
|
|
suffix = '/index.js';
|
|
}
|
|
} catch { /* not a directory; assume .ts file */ }
|
|
return `${p}${spec}${suffix}${q}`;
|
|
});
|
|
writeFileSync(dst, HEADER(relPath) + body);
|
|
generatedFiles.push(dst);
|
|
}
|
|
}
|
|
}
|
|
|
|
mkdirSync(outDir, { recursive: true });
|
|
copyRecursive(tmp, outDir);
|
|
formatGeneratedTypes(generatedFiles);
|
|
rmSync(tmp, { recursive: true, force: true });
|
|
}
|
|
|
|
function main() {
|
|
const bin = resolveCodexBinary();
|
|
const binVersion = readBinaryVersion(bin);
|
|
const pinnedVersion = readPinnedVersion();
|
|
|
|
console.log(`codex binary: ${bin}`);
|
|
console.log(`codex --version: ${binVersion}`);
|
|
console.log(`pinned version: ${pinnedVersion}`);
|
|
|
|
if (!noVersionCheck && binVersion !== pinnedVersion) {
|
|
console.error(`\nVersion mismatch: binary is ${binVersion}, pinned is ${pinnedVersion}.`);
|
|
console.error(`Either update ${path.relative(REPO_ROOT, VERSION_FILE)} or install the matching codex version.`);
|
|
console.error(`Use --no-version-check to override.`);
|
|
process.exit(1);
|
|
}
|
|
|
|
mkdirSync(OUT_DIR, { recursive: true });
|
|
generate(bin, OUT_DIR, binVersion);
|
|
|
|
const count = readdirSync(OUT_DIR, { recursive: true }).filter(f => typeof f === 'string' && f.endsWith('.ts')).length;
|
|
console.log(`\nWrote ${count} files to ${path.relative(REPO_ROOT, OUT_DIR)}`);
|
|
}
|
|
|
|
main();
|