Files
ECC/scripts/lib/context-profile-native-executable.js
T

80 lines
4.6 KiB
JavaScript

'use strict';
const crypto = require('node:crypto');
const fs = require('node:fs');
const path = require('node:path');
const { createRequire } = require('node:module');
const io = require('./context-profile-store-fs');
const cache = new Map();
const MAX_BYTES = 512 * 1024 * 1024;
function nativeFormat(header) {
const hex = header.subarray(0, 4).toString('hex');
return ['7f454c46', 'cffaedfe', 'cefaedfe', 'feedfacf', 'feedface', 'cafebabe', 'bebafeca'].includes(hex)
|| header.subarray(0, 2).toString() === 'MZ';
}
function resolveExecutable(command) {
const candidate = path.isAbsolute(command) ? command : (process.env.PATH || '').split(path.delimiter)
.filter(directory => path.isAbsolute(directory)).map(directory => path.join(directory, process.platform === 'win32' ? 'codex.exe' : 'codex'))
.find(file => fs.existsSync(file));
if (!candidate) throw new Error('Native Codex executable was not found');
let executable = fs.realpathSync(candidate);
const before = io.inspect(executable);
if (!before.stat.isFile() || before.stat.nlink !== 1 || before.stat.size < 4 || before.stat.size > MAX_BYTES) {
throw new Error('Native executable must be a bounded regular file with one link');
}
const header = Buffer.alloc(4);
const fd = fs.openSync(executable, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0) | (fs.constants.O_NONBLOCK || 0));
try {
const opened = fs.fstatSync(fd);
if (opened.dev !== before.stat.dev || opened.ino !== before.stat.ino || !opened.isFile()) throw new Error('Native executable identity changed');
fs.readSync(fd, header, 0, 4, 0); io.recheck(before.chain);
} finally { fs.closeSync(fd); }
if (!nativeFormat(header)) {
// Supported npm distribution: bind its platform binary, never only its JS shim.
if (path.basename(executable) !== 'codex.js') throw new Error('Native adapter requires a native Codex executable');
const packageName = `@openai/codex-${process.platform}-${process.arch}`;
let manifest;
try { manifest = createRequire(executable).resolve(`${packageName}/package.json`); }
catch { throw new Error('Native Codex npm platform package is unavailable'); }
const targets = { 'linux/arm64': 'aarch64-unknown-linux-musl', 'linux/x64': 'x86_64-unknown-linux-musl',
'darwin/arm64': 'aarch64-apple-darwin', 'darwin/x64': 'x86_64-apple-darwin',
'win32/arm64': 'aarch64-pc-windows-msvc', 'win32/x64': 'x86_64-pc-windows-msvc' };
const target = targets[`${process.platform}/${process.arch}`];
if (!target) throw new Error('Unsupported native Codex platform');
executable = fs.realpathSync(path.join(path.dirname(manifest), 'vendor', target, 'bin', process.platform === 'win32' ? 'codex.exe' : 'codex'));
}
return fingerprintExecutable(executable);
}
function fingerprintExecutable(executable) {
const before = io.inspect(executable);
if (!before.stat.isFile() || before.stat.nlink !== 1 || before.stat.size < 4 || before.stat.size > MAX_BYTES) {
throw new Error('Native executable must be a bounded regular file with one link');
}
const identity = [before.stat.dev, before.stat.ino, before.stat.mode, before.stat.size, before.stat.mtimeMs, before.stat.ctimeMs].join(':');
const cached = cache.get(executable);
if (cached?.identity === identity) return cached.value;
const fd = fs.openSync(executable, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0) | (fs.constants.O_NONBLOCK || 0));
try {
const opened = fs.fstatSync(fd);
if (opened.ino !== before.stat.ino || opened.dev !== before.stat.dev || opened.size !== before.stat.size) throw new Error('Native executable changed during verification');
const hash = crypto.createHash('sha256'); const bytes = Buffer.alloc(512 * 1024); let total = 0;
for (let count = fs.readSync(fd, bytes); count; count = fs.readSync(fd, bytes)) {
if (total === 0 && !nativeFormat(bytes.subarray(0, count))) throw new Error('Native executable format is unsupported');
total += count;
if (total > MAX_BYTES) throw new Error('Native executable exceeds the byte bound');
hash.update(bytes.subarray(0, count));
}
const after = fs.fstatSync(fd); io.recheck(before.chain);
if (total !== before.stat.size || after.mtimeMs !== before.stat.mtimeMs || after.ctimeMs !== before.stat.ctimeMs
|| after.size !== before.stat.size) throw new Error('Native executable changed during verification');
const value = { path: executable, bytes: total, digest: hash.digest('hex') };
cache.set(executable, { identity, value });
return value;
} finally { fs.closeSync(fd); }
}
module.exports = { fingerprintExecutable, resolveExecutable };